Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS>google.javascript.jscomp.mozilla.rhino.ast.TryStatement; import com.google.javascript.jscomp.mozilla.rhino.ast.UnaryExpression; import com.google.javascript.jscomp.mozilla.rhino.ast.VariableDeclaration; import com.google.javascript.jscomp.mozilla.rhino.ast.VariableInitializer; import com.google.javascript.jscomp.mozilla.rhino.ast.WhileLoop; import com.google.javascript.jscomp.mozilla.rhino.ast.WithStatement; import com.google.javascript.jscomp.parsing.Config.LanguageMode; import com.google.javascript.rhino.JSDocInfo; import com.google.javascript.rhino.Node; import com.google.javascript.rhino.Token; import java.util.Set; /** * IRFactory transforms the new AST to the old AST. * */ public class IRFactory { private final String sourceString; private final String sourceName; private final Config config; private final ErrorReporter errorReporter; private final TransformDispatcher transformDispatcher; // non-static for thread safety private final Set<String> ALLOWED_DIRECTIVES = Sets.newHashSet("use strict"); private static final Set<String> ES5_RESERVED_KEYWORDS = ImmutableSet.of( // From Section 7.6.1.2 "class", "const", "enum", "export", "extends", "import", "super"); private static final Set<String> ES5_STRICT_RESERVED_KEYWORDS = ImmutableSet.of( // From Section 7.6.1.2 "class", "const", "enum", "export", "extends", "import", "super", "implements", "interface", "let", "package", "private", "protected", "public", "static", "yield"); private final Set<String> reservedKeywords; // @license text gets appended onto the fileLevelJsDocBuilder as found, // and stored in JSDocInfo for placeholder node. Node rootNodeJsDocHolder = new Node(Token.SCRIPT); Node.FileLevelJsDocBuilder fileLevelJsDocBuilder = rootNodeJsDocHolder.getJsDocBuilderForNode(); J

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS>SDocInfo fileOverviewInfo = null; // Use a template node for properties set on all nodes to minimize the // memory footprint associated with these. private Node templateNode; // TODO(johnlenz): Consider creating a template pool for ORIGINALNAME_PROP. private IRFactory(String sourceString, String sourceName, Config config, ErrorReporter errorReporter) { this.sourceString = sourceString; this.sourceName = sourceName; this.config = config; this.errorReporter = errorReporter; this.transformDispatcher = new TransformDispatcher(); // The template node properties are applied to all nodes in this transform. this.templateNode = createTemplateNode(); switch (config.languageMode) { case ECMASCRIPT3: // Reserved words are handled by the Rhino parser. reservedKeywords = null; break; case ECMASCRIPT5: reservedKeywords = ES5_RESERVED_KEYWORDS; break; case ECMASCRIPT5_STRICT: reservedKeywords = ES5_STRICT_RESERVED_KEYWORDS; break; default: throw new IllegalStateException("unknown language mode"); } } // Create a template node to use as a source of common attributes, this allows // the prop structure to be shared among all the node from this source file. // This reduces the cost of these properties to O(nodes) to O(files). private Node createTemplateNode() { // The Node type choice is arbitrary. Node templateNode = new Node(Token.SCRIPT); templateNode.putProp(Node.SOURCENAME_PROP, sourceName); return templateNode; } public static Node transformTree(AstRoot node, String sourceString, Config config, ErrorReporter errorReporter) { IRFactory irFactory = new IRFactory(sourceString, node.getSourceName(), config, errorReporter); Node irNode = irFactory.transform(node); if (node.getComments() != null) { for (Comment comment : node.getComments()) { if (comment.getCommentType() == JSDOC && !comment.isParsed()) { irFactory.handlePossibleFileOverviewJsDoc(comment); } } } irFactory.setFileOverviewJsDoc(irNode); return ir

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS>Node; } private void setFileOverviewJsDoc(Node irNode) { // Only after we've seen all @fileoverview entries, attach the // last one to the root node, and copy the found license strings // to that node. irNode.setJSDocInfo(rootNodeJsDocHolder.getJSDocInfo()); if (fileOverviewInfo != null) { if ((irNode.getJSDocInfo() != null) && (irNode.getJSDocInfo().getLicense() != null)) { fileOverviewInfo.setLicense(irNode.getJSDocInfo().getLicense()); } irNode.setJSDocInfo(fileOverviewInfo); } } private Node transformBlock(AstNode node) { Node irNode = transform(node); if (irNode.getType() != Token.BLOCK) { if (irNode.getType() == Token.EMPTY) { irNode.setType(Token.BLOCK); irNode.setWasEmptyNode(true); } else { Node newBlock = newNode(Token.BLOCK, irNode); newBlock.setLineno(irNode.getLineno()); newBlock.setCharno(irNode.getCharno()); irNode = newBlock; } } return irNode; } /** * @return true if the jsDocParser represents a fileoverview. */ private boolean handlePossibleFileOverviewJsDoc( JsDocInfoParser jsDocParser) { if (jsDocParser.getFileOverviewJSDocInfo() != fileOverviewInfo) { fileOverviewInfo = jsDocParser.getFileOverviewJSDocInfo(); return true; } return false; } private void handlePossibleFileOverviewJsDoc(Comment comment) { JsDocInfoParser jsDocParser = createJsDocInfoParser(comment); comment.setParsed(true); handlePossibleFileOverviewJsDoc(jsDocParser); } private JSDocInfo handleJsDoc(AstNode node) { Comment comment = node.getJsDocNode(); if (comment != null) { JsDocInfoParser jsDocParser = createJsDocInfoParser(comment); comment.setParsed(true); if (!handlePossibleFileOverviewJsDoc(jsDocParser)) {

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS> return jsDocParser.retrieveAndResetParsedJSDocInfo(); } } return null; } irNode.setLineno(lineno); int charno = position2charno(node.getAbsolutePosition()); irNode.setCharno(charno); } } } /** * Creates a JsDocInfoParser and parses the JsDoc string. * * Used both for handling individual JSDoc comments and for handling * file-level JSDoc comments (@fileoverview and @license). * * @param node The JsDoc Comment node to parse. * @return A JSDocInfoParser. Will contain either fileoverview jsdoc, or * normal jsdoc, or no jsdoc (if the method parses to the wrong level). */ private JsDocInfoParser createJsDocInfoParser(Comment node) { String comment = node.getValue(); int lineno = node.getLineno(); int position = node.getAbsolutePosition(); // The JsDocInfoParser expects the comment without the initial '/**'. int numOpeningChars = 3; JsDocInfoParser jsdocParser = new JsDocInfoParser( new JsDocTokenStream(comment.substring(numOpeningChars), lineno, position2charno(position) + numOpeningChars), node, sourceName, config, errorReporter); jsdocParser.setFileLevelJsDocBuilder(fileLevelJsDocBuilder); jsdocParser.setFileOverviewJSDocInfo(fileOverviewInfo); jsdocParser.parse(); return jsdocParser; } private int position2charno(int position) { int lineIndex = sourceString.lastIndexOf('\n', position); if (lineIndex == -1) { return position; } else { // Subtract one for initial position being 0. return position - lineIndex - 1; } } private Node justTransform(AstNode node) { return transformDispatcher.process(node); } private class TransformDispatcher extends TypeSafeDispatcher<Node> { private Node processGeneric( com.google.javascript.jscomp.mozilla.rhino.Node n) { Node node = newNode(transformTokenType(n.getType())); return node; } @Override Node process

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS> processKeywordLiteral(KeywordLiteral literalNode) { return newNode(transformTokenType(literalNode.getType())); } @Override Node processLabel(Label labelNode) { return newStringNode(Token.LABEL_NAME, labelNode.getName()); } @Override Node processLabeledStatement(LabeledStatement statementNode) { Node node = newNode(Token.LABEL); Node prev = null; Node cur = node; for (Label label : statementNode.getLabels()) { if (prev != null) { prev.addChildToBack(cur); } cur.addChildToBack(transform(label)); cur.setLineno(label.getLineno()); int clauseAbsolutePosition = position2charno(label.getAbsolutePosition()); cur.setCharno(clauseAbsolutePosition); prev = cur; cur = newNode(Token.LABEL); } prev.addChildToBack(transform(statementNode.getStatement())); return node; } @Override Node processName(Name nameNode) { return processName(nameNode, false); } Node processName(Name nameNode, boolean asString) { if (asString) { return newStringNode(Token.STRING, nameNode.getIdentifier()); } else { if (isReservedKeyword(nameNode.getIdentifier())) { errorReporter.error( "identifier is a reserved word", sourceName, nameNode.getLineno(), "", 0); } return newStringNode(Token.NAME, nameNode.getIdentifier()); } } /** * @return Whether the */ private boolean isReservedKeyword(String identifier) { return reservedKeywords != null && reservedKeywords.contains(identifier); } @Override Node processNewExpression(NewExpression exprNode) { return processFunctionCall(exprNode); } @Override Node processNumberLiteral(NumberLiteral literalNode) { return newNumberNode(literalNode.getNumber()); } @Override Node processObjectLiteral(ObjectLiteral literalNode) { if (literalNode.isDestructuring()) { reportDestructuringAssign(literalNode); } Node node = newNode(Token.OBJECTLIT); for (ObjectProperty el : literalNode.getElements()) { if (config.languageMode == LanguageMode.E

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS>CMASCRIPT3) { if (el.isGetter()) { reportGetter(el); continue; } else if (el.isSetter()) { reportSetter(el); continue; } } Node key = transformAsString(el.getLeft()); Node value = transform(el.getRight()); if (el.isGetter()) { key.setType(Token.GET); Preconditions.checkState(value.getType() == Token.FUNCTION); if (getFnParamNode(value).hasChildren()) { reportGetterParam(el.getLeft()); } } else if (el.isSetter()) { key.setType(Token.SET); Preconditions.checkState(value.getType() == Token.FUNCTION); if (!getFnParamNode(value).hasOneChild()) { reportSetterParam(el.getLeft()); } } key.addChildToFront(value); node.addChildToBack(key); } return node; } /** * @param fnNode The function. * @return The Node containing the Function parameters. */ Node getFnParamNode(Node fnNode) { // Function NODE: [ FUNCTION -> NAME, LP -> ARG1, ARG2, ... ] Preconditions.checkArgument(fnNode.getType() == Token.FUNCTION); return fnNode.getFirstChild().getNext(); } @Override Node processObjectProperty(ObjectProperty propertyNode) { return processInfixExpression(propertyNode); } @Override Node processParenthesizedExpression(ParenthesizedExpression exprNode) { Node node = transform(exprNode.getExpression()); node.putProp(Node.PARENTHESIZED_PROP, Boolean.TRUE); return node; } @Override Node processPropertyGet(PropertyGet getNode) { return newNode( Token.GETPROP, transform(getNode.getTarget()), transformAsString(getNode.getProperty())); } @Override Node processRegExpLiteral(RegExpLiteral literalNode) { Node literalStringNode = newStringNode(literalNode.getValue()); // assume it's on the same line. literalStringNode.setLineno(literalNode.getLineno()); Node node = newNode(Token.REGEXP, literalStringNode); String flags = literalNode.getFlags(); if (flags !=

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS> null && !flags.isEmpty()) { Node flagsNode = newStringNode(flags); // Assume the flags are on the same line as the literal node. flagsNode.setLineno(literalNode.getLineno()); node.addChildToBack(flagsNode); } return node; } @Override Node processReturnStatement(ReturnStatement statementNode) { Node node = newNode(Token.RETURN); if (statementNode.getReturnValue() != null) { node.addChildToBack(transform(statementNode.getReturnValue())); } return node; } @Override Node processScope(Scope scopeNode) { return processGeneric(scopeNode); } @Override Node processStringLiteral(StringLiteral literalNode) { Node n = newStringNode(literalNode.getValue()); return n; } @Override Node processSwitchCase(SwitchCase caseNode) { Node node; if (caseNode.isDefault()) { node = newNode(Token.DEFAULT); } else { AstNode expr = caseNode.getExpression(); node = newNode(Token.CASE, transform(expr)); } Node block = newNode(Token.BLOCK); block.putBooleanProp(Node.SYNTHETIC_BLOCK_PROP, true); block.setLineno(caseNode.getLineno()); block.setCharno(position2charno(caseNode.getAbsolutePosition())); if (caseNode.getStatements() != null) { for (AstNode child : caseNode.getStatements()) { block.addChildToBack(transform(child)); } } node.addChildToBack(block); return node; } @Override Node processSwitchStatement(SwitchStatement statementNode) { Node node = newNode(Token.SWITCH, transform(statementNode.getExpression())); for (AstNode child : statementNode.getCases()) { node.addChildToBack(transform(child)); } return node; } @Override Node processThrowStatement(ThrowStatement statementNode) { return newNode(Token.THROW, transform(statementNode.getExpression())); } @Override Node processTryStatement(TryStatement statementNode) { Node node = newNode(Token.TRY, transformBlock(statementNode.getTryBlock()));

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS> return Token.XMLATTR; case com.google.javascript.jscomp.mozilla.rhino.Token.XMLEND: return Token.XMLEND; case com.google.javascript.jscomp.mozilla.rhino.Token.TO_OBJECT: return Token.TO_OBJECT; case com.google.javascript.jscomp.mozilla.rhino.Token.TO_DOUBLE: return Token.TO_DOUBLE; case com.google.javascript.jscomp.mozilla.rhino.Token.GET: return Token.GET; case com.google.javascript.jscomp.mozilla.rhino.Token.SET: return Token.SET; case com.google.javascript.jscomp.mozilla.rhino.Token.CONST: return Token.CONST; case com.google.javascript.jscomp.mozilla.rhino.Token.SETCONST: return Token.SETCONST; case com.google.javascript.jscomp.mozilla.rhino.Token.DEBUGGER: return Token.DEBUGGER; } // Token without name throw new IllegalStateException(String.valueOf(token)); } // Simple helper to create nodes and set the initial node properties. private Node newNode(int type) { return new Node(type).clonePropsFrom(templateNode); } private Node newNode(int type, Node child1) { return new Node(type, child1).clonePropsFrom(templateNode); } private Node newNode(int type, Node child1, Node child2) { return new Node(type, child1, child2).clonePropsFrom(templateNode); } private Node newNode(int type, Node child1, Node child2, Node child3) { return new Node(type, child1, child2, child3).clonePropsFrom(templateNode); } private Node newStringNode(String value) { return Node.newString(value).clonePropsFrom(templateNode); } private Node newStringNode(int type, String value) { return Node.newString(type, value).clonePropsFrom(templateNode); } private Node newNumberNode(Double value) { return Node.newNumber(value).clonePropsFrom(templateNode); } }

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS> in a prototype definition and as an object * literal key, we rename it only if it satisifies both renaming policies. * */ class RenamePrototypes implements CompilerPass { private final AbstractCompiler compiler; private final boolean aggressiveRenaming; private final char[] reservedCharacters; /** Previously used prototype renaming map. */ private final VariableMap prevUsedRenameMap; /** * The Property class encapsulates the information needed for renaming * a method or member. */ private class Property { String oldName; String newName; int prototypeCount; int objLitCount; int refCount; Property(String name) { this.oldName = name; this.newName = null; this.prototypeCount = 0; this.objLitCount = 0; this.refCount = 0; } int count() { return prototypeCount + objLitCount + refCount; } boolean canRename() { if (this.prototypeCount > 0 && this.objLitCount == 0) { return canRenamePrototypeProperty(); } if (this.objLitCount > 0 && this.prototypeCount == 0) { return canRenameObjLitProperty(); } // We're not sure what kind of property this is, so we're conservative. // Note that we still want to try renaming the property even when both // counts are zero. It may be a property added to an object at runtime, // like: o.newProp = x; return canRenamePrototypeProperty() && canRenameObjLitProperty(); } private boolean canRenamePrototypeProperty() { if (compiler.getCodingConvention().isExported(oldName)) { // an externally visible name should not be renamed. return false; } if (compiler.getCodingConvention().isPrivate(oldName)) { // private names can be safely renamed. Rename! return true; } if (aggressiveRenaming) { return true; } for (int i = 0, n = oldName.length(); i < n; i++) { char ch = oldName.charAt(i); if (Character.isUpperCase(ch) || !Character.isLetter(ch)) { return true; }

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS>(first); add("]"); break; case Token.LP: add("("); addList(first); add(")"); break; case Token.COMMA: Preconditions.checkState(childCount == 2); addList(first, false, context); break; case Token.NUMBER: Preconditions.checkState( childCount == ((n.getParent() != null && n.getParent().getType() == Token.OBJECTLIT) ? 1 : 0)); cc.addNumber(n.getDouble()); break; case Token.TYPEOF: case Token.VOID: case Token.NOT: case Token.BITNOT: case Token.POS: { // All of these unary operators are right-associative Preconditions.checkState(childCount == 1); cc.addOp(NodeUtil.opToStrNoFail(type), false); addExpr(first, NodeUtil.precedence(type)); break; } case Token.NEG: { Preconditions.checkState(childCount == 1); // It's important to our sanity checker that the code // we print produces the same AST as the code we parse back. // NEG is a weird case because Rhino parses "- -2" as "2". if (n.getFirstChild().getType() == Token.NUMBER) { cc.addNumber(-n.getFirstChild().getDouble()); } else { cc.addOp(NodeUtil.opToStrNoFail(type), false); addExpr(first, NodeUtil.precedence(type)); } break; } case Token.HOOK: { Preconditions.checkState(childCount == 3); int p = NodeUtil.precedence(type); addLeftExpr(first, p + 1, context); cc.addOp("?", true); addExpr(first.getNext(), 1); cc.addOp(":", true); addExpr(last, 1); break; } case Token.REGEXP: if (first.getType() != Token.STRING || last.getType() != Token.STRING) { throw new Error("Expected children to be strings"); } String regexp = regexpEscape(first.getString(), outputCharsetEncoder); // I only use

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS> * node with the associated skipIndexes array. This is a space optimization * since we avoid creating a whole Node object for each empty array literal * slot. * @param firstInList The first in the node list (chained through the next * property). */ void addArrayList(Node firstInList) { boolean lastWasEmpty = false; for (Node n = firstInList; n != null; n = n.getNext()) { if (n != firstInList) { cc.listSeparator(); } addExpr(n, 1); lastWasEmpty = n.getType() == Token.EMPTY; } if (lastWasEmpty) { cc.listSeparator(); } } void addCaseBody(Node caseBody) { cc.beginCaseBody(); add(caseBody); cc.endCaseBody(); } void addAllSiblings(Node n) { for (Node c = n; c != null; c = c.getNext()) { add(c); } } /** Outputs a js string, using the optimal (single/double) quote character */ static String jsString(String s, CharsetEncoder outputCharsetEncoder) { int singleq = 0, doubleq = 0; // could count the quotes and pick the optimal quote character for (int i = 0; i < s.length(); i++) { switch (s.charAt(i)) { case '"': doubleq++; break; case '\'': singleq++; break; } } String doublequote, singlequote; char quote; if (singleq < doubleq) { // more double quotes so escape the single quotes quote = '\''; doublequote = "\""; singlequote = "\\\'"; } else { // more single quotes so escape the doubles quote = '\"'; doublequote = "\\\""; singlequote = "\'"; } return strEscape(s, quote, doublequote, singlequote, "\\\\", outputCharsetEncoder); } /** Escapes regular expression */ static String regexpEscape(String s, CharsetEncoder outputCharsetEncoder) { return strEscape(s, '/', "\"", "'", "\\", outputCharsetEncoder); } /** * Escapes the given string to a double quoted (

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS>") JavaScript/JSON string */ static String escapeToDoubleQuotedJsString(String s) { return strEscape(s, '"', "\\\"", "\'", "\\\\", null); } /* If the user doesn't want to specify an output charset encoder, assume they want Latin/ASCII characters only. */ static String regexpEscape(String s) { return regexpEscape(s, null); } /** Helper to escape javascript string as well as regular expression */ static String strEscape(String s, char quote, String doublequoteEscape, String singlequoteEscape, String backslashEscape, CharsetEncoder outputCharsetEncoder) { StringBuilder sb = new StringBuilder(s.length() + 2); sb.append(quote); for (int i = 0; i < s.length(); i++) { char c = s.charAt(i); switch (c) { case '\0': sb.append("\\0"); break; case '\n': sb.append("\\n"); break; case '\r': sb.append("\\r"); break; case '\t': sb.append("\\t"); break; case '\\': sb.append(backslashEscape); break; case '\"': sb.append(doublequoteEscape); break; case '\'': sb.append(singlequoteEscape); break; case '>': // Break --> into --\> or ]]> into ]]\> if (i >= 2 && ((s.charAt(i - 1) == '-' && s.charAt(i - 2) == '-') || (s.charAt(i - 1) == ']' && s.charAt(i - 2) == ']'))) { sb.append("\\>"); } else { sb.append(c); } break; case '<': // Break </script into <\/script final String END_SCRIPT = "/script"; // Break <!-- into <\!-- final String START_COMMENT = "!--"; if (s.regionMatches(true, i + 1, END_SCRIPT, 0, END_SCRIPT.length())) { sb.append("<\\"); } else if (s.regionMatches(false, i + 1, START_COMMENT, 0, START_COMMENT.length())) { sb.append("<\\

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS>"); } else { sb.append(c); } break; default: // If we're given an outputCharsetEncoder, then check if the // character can be represented in this character set. if (outputCharsetEncoder != null) { if (outputCharsetEncoder.canEncode(c)) { sb.append(c); } else { // Unicode-escape the character. appendHexJavaScriptRepresentation(sb, c); } } else { // No charsetEncoder provided - pass straight latin characters // through, and escape the rest. Doing the explicit character // check is measurably faster than using the CharsetEncoder. if (c > 0x1f && c <= 0x7f) { sb.append(c); } else { // Other characters can be misinterpreted by some js parsers, // or perhaps mangled by proxies along the way, // so we play it safe and unicode escape them. appendHexJavaScriptRepresentation(sb, c); } } } } sb.append(quote); return sb.toString(); } static String identifierEscape(String s) { // First check if escaping is needed at all -- in most cases it isn't. if (NodeUtil.isLatin(s)) { return s; } // Now going through the string to escape non-latin characters if needed. StringBuilder sb = new StringBuilder(); for (int i = 0; i < s.length(); i++) { char c = s.charAt(i); // Identifiers should always go to Latin1/ ASCII characters because // different browser's rules for valid identifier characters are // crazy. if (c > 0x1F && c < 0x7F) { sb.append(c); } else { appendHexJavaScriptRepresentation(sb, c); } } return sb.toString(); } /** * @param maxCount The maximum number of children to look for. * @return The number of children of this node that are non empty up to * maxCount. */ private static int getNonEmptyChildCount(Node n, int maxCount) { int i = 0; Node c = n.getFirstChild(); for (; c

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS>can.c and jsscan.h * in the jsref package. * * @see Parser * */ public class TokenStream { /* * For chars - because we need something out-of-range * to check. (And checking EOF by exception is annoying.) * Note distinction from EOF token type! */ private final static int EOF_CHAR = -1; public TokenStream(Parser parser, Reader sourceReader, String sourceString, int lineno) { this.parser = parser; this.lineno = lineno; if (sourceReader != null) { if (sourceString != null) Kit.codeBug(); this.sourceReader = sourceReader; this.sourceBuffer = new char[512]; this.sourceEnd = 0; } else { if (sourceString == null) Kit.codeBug(); this.sourceString = sourceString; this.sourceEnd = sourceString.length(); } this.sourceCursor = 0; } /* This function uses the cached op, string and number fields in * TokenStream; if getToken has been called since the passed token * was scanned, the op or string printed may be incorrect. */ String tokenToString(int token) { if (Token.printTrees) { String name = Token.name(token); switch (token) { case Token.STRING: case Token.REGEXP: case Token.NAME: return name + " `" + this.string + "'"; case Token.NUMBER: return "NUMBER " + this.number; } return name; } return ""; } public static boolean isKeyword(String s) { return Token.EOF != stringToKeyword(s); } private static int stringToKeyword(String name) { // #string_id_map# // The following assumes that Token.EOF == 0 final int Id_break = Token.BREAK, Id_case = Token.CASE, Id_continue = Token.CONTINUE, Id_default = Token.DEFAULT, Id_delete = Token.DELPROP, Id_do = Token.DO, Id_else = Token.ELSE, Id_export

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS>.RESERVED, Id_transient = Token.RESERVED, Id_try = Token.TRY, Id_volatile = Token.RESERVED; int id; String s = name; // #generated# Last update: 2001-06-01 17:45:01 CEST L0: { id = 0; String X = null; int c; L: switch (s.length()) { case 2: c=s.charAt(1); if (c=='f') { if (s.charAt(0)=='i') {id=Id_if; break L0;} } else if (c=='n') { if (s.charAt(0)=='i') {id=Id_in; break L0;} } else if (c=='o') { if (s.charAt(0)=='d') {id=Id_do; break L0;} } break L; case 3: switch (s.charAt(0)) { case 'f': if (s.charAt(2)=='r' && s.charAt(1)=='o') { id=Id_for; break L0; } break L; case 'i': if (s.charAt(2)=='t' && s.charAt(1)=='n') { id=Id_int; break L0; } break L; case 'n': if (s.charAt(2)=='w' && s.charAt(1)=='e') { id=Id_new; break L0; } break L; case 't': if (s.charAt(2)=='y' && s.charAt(1)=='r') { id=Id_try; break L0; } break L; case 'v': if (s.charAt(2)=='r' && s.charAt(1)=='a') { id=Id_var; break L0; } break L; } break L; case 4: switch (s.charAt(0)) { case 'b': X="byte";id=Id_byte; break L; case 'c': c=s.charAt(3); if (c=='e') { if (s.

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS>charAt(2)=='s' && s.charAt(1)=='a') { id=Id_case; break L0;} } else if (c=='r') { if (s.charAt(2)=='a' && s.charAt(1)=='h') { id=Id_char; break L0; } } break L; case 'e': c=s.charAt(3); if (c=='e') { if (s.charAt(2)=='s' && s.charAt(1)=='l') { id=Id_else; break L0;} } else if (c=='m') { if (s.charAt(2)=='u' && s.charAt(1)=='n') { id=Id_enum; break L0;} } break L; case 'g': X="goto";id=Id_goto; break L; case 'l': X="long";id=Id_long; break L; case 'n': X="null";id=Id_null; break L; case 't': c=s.charAt(3); if (c=='e') { if (s.charAt(2)=='u' && s.charAt(1)=='r') { id=Id_true; break L0;} } else if (c=='s') { if (s.charAt(2)=='i' && s.charAt(1)=='h') { id=Id_this; break L0;} } break L; case 'v': X="void";id=Id_void; break L; case 'w': X="with";id=Id_with; break L; } break L; case 5: switch (s.charAt(2)) { case 'a': X="class";id=Id_class; break L; case 'e': X="break";id=Id_break; break L; case 'i': X="while";id=Id_while; break L; case 'l': X="false";id=Id_false; break L; case 'n': c=s.charAt(0); if (c=='c') { X="const";id=Id_const; } else if (c=='f') {

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS> X="final";id=Id_final; } break L; case 'o': c=s.charAt(0); if (c=='f') { X="float";id=Id_float; } else if (c=='s') { X="short";id=Id_short; } break L; case 'p': X="super";id=Id_super; break L; case 'r': X="throw";id=Id_throw; break L; case 't': X="catch";id=Id_catch; break L; } break L; case 6: switch (s.charAt(1)) { case 'a': X="native";id=Id_native; break L; case 'e': c=s.charAt(0); if (c=='d') { X="delete";id=Id_delete; } else if (c=='r') { X="return";id=Id_return; } break L; case 'h': X="throws";id=Id_throws; break L; case 'm': X="import";id=Id_import; break L; case 'o': X="double";id=Id_double; break L; case 't': X="static";id=Id_static; break L; case 'u': X="public";id=Id_public; break L; case 'w': X="switch";id=Id_switch; break L; case 'x': X="export";id=Id_export; break L; case 'y': X="typeof";id=Id_typeof; break L; } break L; case 7: switch (s.charAt(1)) { case 'a': X="package";id=Id_package; break L; case 'e': X="default";id=Id_default; break L; case 'i': X="finally";id=Id_finally; break L; case 'o': X="boolean";id=Id_boolean; break L; case 'r': X="private";id=Id_private; break L; case 'x': X="extends";id=Id_extends; break L; } break L; case 8: switch (s.charAt(0)) {

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS> case 'a': X="abstract";id=Id_abstract; break L; case 'c': X="continue";id=Id_continue; break L; case 'd': X="debugger";id=Id_debugger; break L; case 'f': X="function";id=Id_function; break L; case 'v': X="volatile";id=Id_volatile; break L; } break L; case 9: c=s.charAt(0); if (c=='i') { X="interface";id=Id_interface; } else if (c=='p') { X="protected";id=Id_protected; } else if (c=='t') { X="transient";id=Id_transient; } break L; case 10: c=s.charAt(1); if (c=='m') { X="implements";id=Id_implements; } else if (c=='n') { X="instanceof";id=Id_instanceof; } break L; case 12: X="synchronized";id=Id_synchronized; break L; } if (X!=null && X!=s && !X.equals(s)) id = 0; } // #/generated# // #/string_id_map# if (id == 0) { return Token.EOF; } return id & 0xff; } public static boolean isJSIdentifier(String s) { int length = s.length(); if (length == 0 || !Character.isJavaIdentifierStart(s.charAt(0))) return false; for (int i=1; i<length; i++) { char c = s.charAt(i); if (!Character.isJavaIdentifierPart(c)) { if (c == '\\') { if (! ((i + 5) < length) && (s.charAt(i + 1) == 'u') && 0 <= Kit.xDigitToInt(s.charAt(i + 2), 0) && 0 <= Kit.xDigitToInt(s.charAt(i + 3), 0) && 0 <= Kit.xDigitToInt(s.charAt(i + 4), 0)

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS> && 0 <= Kit.xDigitToInt(s.charAt(i + 5), 0)) { return true; } } return false; } } return true; } protected final int getLineno() { return lineno; } protected final int getCharno() { return charno; } final String getString() { return string; } final double getNumber() { return number; } final boolean eof() { return hitEOF; } public final int getToken() throws IOException { tokenno++; // Check for pushed-back token if (this.pushbackToken != Token.EOF) { int result = this.pushbackToken; this.pushbackToken = Token.EOF; return result; } int c; retry: for (;;) { // Eat whitespace, possibly sensitive to newlines. for (;;) { charno = -1; c = getChar(); if (c == EOF_CHAR) { return Token.EOF; } else if (c == '\n') { dirtyLine = false; return Token.EOL; } else if (!isJSSpace(c)) { if (c != '-') { dirtyLine = true; } break; } } if (c == '@') return Token.XMLATTR; // identifier/keyword/instanceof? // watch out for starting with a <backslash> boolean identifierStart; boolean isUnicodeEscapeStart = false; if (c == '\\') { c = getChar(); if (c == 'u') { identifierStart = true; isUnicodeEscapeStart = true; stringBufferTop = 0; } else { identifierStart = false; ungetChar(c); c = '\\'; } } else { identifierStart = Character.isJavaIdentifierStart((char)c); if (identifierStart) { stringBufferTop = 0; addToString(c); } } if (identifierStart) { boolean containsEscape = isUnicodeEscapeStart; for (;;) { if (isUnicodeEscapeStart) { // strictly speaking we should probably push-back // all the bad characters if the <backslash>uXXXX // sequence is malformed. But since there isn't a //

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS>') { c = getChar(); if (c == 'x' || c == 'X') { base = 16; c = getChar(); } else if (isDigit(c)) { base = 8; } else { addToString('0'); } } if (base == 16) { while (0 <= Kit.xDigitToInt(c, 0)) { addToString(c); c = getChar(); } } else { while ('0' <= c && c <= '9') { /* * We permit 08 and 09 as decimal numbers, which * makes our behavior a superset of the ECMA * numeric grammar. We might not always be so * permissive, so we warn about it. */ if (base == 8 && c >= '8') { parser.addWarning("msg.bad.octal.literal", c == '8' ? "8" : "9"); base = 10; } addToString(c); c = getChar(); } } boolean isInteger = true; if (base == 10 && (c == '.' || c == 'e' || c == 'E')) { isInteger = false; if (c == '.') { do { addToString(c); c = getChar(); } while (isDigit(c)); } if (c == 'e' || c == 'E') { addToString(c); c = getChar(); if (c == '+' || c == '-') { addToString(c); c = getChar(); } if (!isDigit(c)) { parser.addError("msg.missing.exponent"); return Token.ERROR; } do { addToString(c); c = getChar(); } while (isDigit(c)); } } ungetChar(c); String numString = getStringFromBuffer(); double dval; if (base == 10 && !isInteger) { try { // Use Java conversion to number from string... dval = Double.valueOf(numString).doubleValue(); } catch (NumberFormatException ex) { parser.addError("

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS> return '0' <= c && c <= '9'; } /** * Tests whether the character is a valid JavaScript white space character * as defined in ECMAScript 3rd edition. * * Note: jsscan.c uses C isspace() (which allows * \v, I think.) note that code in getChar() implicitly accepts * '\r' == \u000D as well. */ static boolean isJSSpace(int c) { if (c <= 127) { return c == 0x20 || c == 0x9 || c == 0xC || c == 0xB; } else { return c == 0xA0 || Character.getType((char)c) == Character.SPACE_SEPARATOR; } } private static boolean isJSFormatChar(int c) { return c > 127 && Character.getType((char)c) == Character.FORMAT; } /** * Gets the accumulated {@link JSDocInfo} and resets it. * Obsolete */ JSDocInfo getAndResetJSDocInfo() { return null; } /** * Returns any {@link JSDocInfo} with a fileoverview tag that showed up. * Obsolete */ JSDocInfo getFileOverviewJSDocInfo() { return null; } /** * Returns whether any {@link JSDocInfo} was accumulated. * Obsolete */ boolean isPopulated() { return false; } /** * Parser calls the method when it gets / or /= in literal context. */ void readRegExp(int startToken) throws IOException { stringBufferTop = 0; if (startToken == Token.ASSIGN_DIV) { // Miss-scanned /= addToString('='); } else { if (startToken != Token.DIV) Kit.codeBug(); } boolean inCharSet = false; // true if inside a '['..']' pair int c; while ((c = getChar()) != '/' || inCharSet) { if (c == '\n' || c == EOF_CHAR) { ungetChar(c); throw parser.reportError("msg

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS> return true; } else { continue; } } c = getChar(); } stringBufferTop = 0; // throw away the string in progress this.string = null; parser.addError("msg.XML.bad.form"); return false; } /** * */ private boolean readEntity() throws IOException { int declTags = 1; for (int c = getChar(); c != EOF_CHAR; c = getChar()) { addToString(c); switch (c) { case '<': declTags++; break; case '>': declTags--; if (declTags == 0) return true; break; } } stringBufferTop = 0; // throw away the string in progress this.string = null; parser.addError("msg.XML.bad.form"); return false; } /** * */ private boolean readPI() throws IOException { for (int c = getChar(); c != EOF_CHAR; c = getChar()) { addToString(c); if (c == '?' && peekChar() == '>') { c = getChar(); // Skip > addToString(c); return true; } } stringBufferTop = 0; // throw away the string in progress this.string = null; parser.addError("msg.XML.bad.form"); return false; } private String getStringFromBuffer() { return new String(stringBuffer, 0, stringBufferTop); } private void addToString(int c) { int N = stringBufferTop; if (N == stringBuffer.length) { char[] tmp = new char[stringBuffer.length * 2]; System.arraycopy(stringBuffer, 0, tmp, 0, N); stringBuffer = tmp; } stringBuffer[N] = (char)c; stringBufferTop = N + 1; } public int getTokenno() { return tokenno; } protected void ungetChar(int c) { // cannot unread past across line boundary if (ungetCursor != 0 && ungetBuffer[ungetCursor - 1] == '\n') Kit.codeBug

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS>(); ungetBuffer[ungetCursor++] = c; } private boolean matchChar(int test) throws IOException { int c = getChar(); if (c == test) { return true; } else { ungetChar(c); return false; } } private int peekChar() throws IOException { int c = getChar(); ungetChar(c); return c; } protected int getChar() throws IOException { if (ungetCursor != 0) { --ungetCursor; if (charno == -1) { charno = getOffset(); } return ungetBuffer[ungetCursor]; } for(;;) { int c; if (sourceString != null) { if (sourceCursor == sourceEnd) { hitEOF = true; if (charno == -1) { charno = getOffset(); } return EOF_CHAR; } c = sourceString.charAt(sourceCursor++); } else { if (sourceCursor == sourceEnd) { if (!fillSourceBuffer()) { hitEOF = true; if (charno == -1) { charno = getOffset(); } return EOF_CHAR; } } c = sourceBuffer[sourceCursor++]; } if (lineEndChar >= 0) { if (lineEndChar == '\r' && c == '\n') { lineEndChar = '\n'; continue; } lineEndChar = -1; lineStart = sourceCursor - 1; lineno++; } if (c <= 127) { if (c == '\n' || c == '\r') { lineEndChar = c; c = '\n'; } } else { if (isJSFormatChar(c)) { continue; } if (ScriptRuntime.isJSLineTerminator(c)) { lineEndChar = c; c = '\n'; } } if (charno == -1) { charno = getOffset(); } return c; } } private void skipLine() throws IOException { // skip to end of line int c; while ((c = getChar()) != EOF

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS>_CHAR && c != '\n') { } ungetChar(c); } final int getOffset() { return sourceCursor - lineStart - ungetCursor - 1; } final String getLine() { if (sourceString != null) { // String case int lineEnd = sourceCursor; if (lineEndChar >= 0) { --lineEnd; } else { for (; lineEnd != sourceEnd; ++lineEnd) { int c = sourceString.charAt(lineEnd); if (ScriptRuntime.isJSLineTerminator(c)) { break; } } } return sourceString.substring(lineStart, lineEnd); } else { // Reader case int lineLength = sourceCursor - lineStart; if (lineEndChar >= 0) { --lineLength; } else { // Read until the end of line for (;; ++lineLength) { int i = lineStart + lineLength; if (i == sourceEnd) { try { if (!fillSourceBuffer()) { break; } } catch (IOException ioe) { // ignore it, we're already displaying an error... break; } // i recalculuation as fillSourceBuffer can move saved // line buffer and change lineStart i = lineStart + lineLength; } int c = sourceBuffer[i]; if (ScriptRuntime.isJSLineTerminator(c)) { break; } } } return new String(sourceBuffer, lineStart, lineLength); } } private boolean fillSourceBuffer() throws IOException { if (sourceString != null) Kit.codeBug(); if (sourceEnd == sourceBuffer.length) { if (lineStart != 0) { System.arraycopy(sourceBuffer, lineStart, sourceBuffer, 0, sourceEnd - lineStart); sourceEnd -= lineStart; sourceCursor -= lineStart; lineStart = 0; } else { char[] tmp = new char[sourceBuffer.length * 2]; System.arraycopy(sourceBuffer, 0, tmp, 0, sourceEnd); sourceBuffer = tmp; } } int n = sourceReader.read(sourceBuffer, sourceEnd,

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS> sourceBuffer.length - sourceEnd); if (n < 0) { return false; } sourceEnd += n; return true; } /** * Set the FileLevelJsDocBuilder on the TokenStream. The TokenStream passes * the builder on to the JSDocInfoParser if it exists. Otherwise this method * is a no-op. * @param fileLevelJsDocBuilder */ public void setFileLevelJsDocBuilder( Node.FileLevelJsDocBuilder fileLevelJsDocBuilder) { } // stuff other than whitespace since start of line private boolean dirtyLine; String regExpFlags; private int pushbackToken; private int tokenno; // Set this to an inital non-null value so that the Parser has // something to retrieve even if an error has occured and no // string is found. Fosters one class of error, but saves lots of // code. private String string = ""; private double number; private char[] stringBuffer = new char[128]; private int stringBufferTop; private ObjToIntMap allStrings = new ObjToIntMap(50); // Room to backtrace from to < on failed match of the last - in <!-- private final int[] ungetBuffer = new int[3]; private int ungetCursor; private boolean hitEOF = false; private int lineStart = 0; private int lineno; private int charno = -1; private int lineEndChar = -1; private String sourceString; private Reader sourceReader; private char[] sourceBuffer; private int sourceEnd; private int sourceCursor; // for xml tokenizer private boolean xmlIsAttribute; private boolean xmlIsTagContent; private int xmlOpenTagsCount; private Parser parser; }

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS>REGION_LENGTH + 1) / 2 + 1); for (int n = 1; n < startLine; n++) { int nextpos = js.indexOf('\n', pos); if (nextpos == -1) { break; } pos = nextpos + 1; } int end = pos; int endLine = startLine; for (int n = 0; n < SOURCE_EXCERPT_REGION_LENGTH; n++, endLine++) { end = js.indexOf('\n', end); if (end == -1) { break; } end++; } if (lineNumber >= endLine) { return null; } if (end == -1) { int last = js.length() - 1; if (js.charAt(last) == '\n') { return new SimpleRegion(startLine, endLine, js.substring(pos, last)); } else { return new SimpleRegion(startLine, endLine, js.substring(pos)); } } else { return new SimpleRegion(startLine, endLine, js.substring(pos, end)); } } @Override public String toString() { return fileName; } public static SourceFile fromFile(String fileName, Charset c) { return fromFile(new File(fileName), c); } public static SourceFile fromFile(String fileName) { return fromFile(new File(fileName)); } public static SourceFile fromFile(File file, Charset c) { return new OnDisk(file, c); } public static SourceFile fromFile(File file) { return new OnDisk(file); } public static SourceFile fromCode(String fileName, String code) { return new Preloaded(fileName, code); } public static SourceFile fromCode(String fileName, String originalPath, String code) { return new Preloaded(fileName, originalPath, code); } public static SourceFile fromInputStream(String fileName, InputStream s) throws IOException { return fromCode(fileName, CharStreams.toString(new InputStreamReader(s, Charsets.UTF_8))); } public static SourceFile fromInputStream(String fileName, String originalPath, InputStream s) throws

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS> IOException { return fromCode(fileName, originalPath, CharStreams.toString(new InputStreamReader(s, Charsets.UTF_8))); } public static SourceFile fromReader(String fileName, Reader r) throws IOException { return fromCode(fileName, CharStreams.toString(r)); } public static SourceFile fromGenerator(String fileName, Generator generator) { return new Generated(fileName, generator); } ////////////////////////////////////////////////////////////////////////////// // Implementations /** * A source file where the code has been preloaded. */ static class Preloaded extends SourceFile { Preloaded(String fileName, String code) { this(fileName, fileName, code); } Preloaded(String fileName, String originalPath, String code) { super(fileName); super.setOriginalPath(originalPath); super.setCode(code); } } /** * A source file where the code will be dynamically generated * from the injected interface. */ static class Generated extends SourceFile { private final Generator generator; // Not private, so that LazyInput can extend it. Generated(String fileName, Generator generator) { super(fileName); this.generator = generator; } @Override public synchronized String getCode() throws IOException { String cachedCode = super.getCode(); if (cachedCode == null) { cachedCode = generator.getCode(); super.setCode(cachedCode); } return cachedCode; } // Clear out the generated code when finished with a compile; we can // regenerate it if we ever need it again. @Override public void clearCachedSource() { super.setCode(null); } } /** * A source file where the code is only read into memory if absolutely * necessary. We will try to delay loading the code into memory as long as * possible. */ static class OnDisk extends SourceFile { private final File file; // This is stored as a String, but passed in and out as a Charset so that // we can serialize the class. // Default input file format for JSCompiler has always been UTF_8. protected String inputCharset = Charsets.UTF_8.name(); OnDisk(File file, Charset c) { this(file); if (c != null) {

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS> this.setCharset(c); } } // No Charset provided? OnDisk(File file) { super(file.getPath()); this.file = file; } @Override public synchronized String getCode() throws IOException { String cachedCode = super.getCode(); if (cachedCode == null) { cachedCode = Files.toString(file, this.getCharset()); super.setCode(cachedCode); } return cachedCode; } /** * Gets a reader for the code in this source file. */ @Override public Reader getCodeReader() throws IOException { if (hasSourceInMemory()) { return super.getCodeReader(); } else { // If we haven't pulled the code into memory yet, don't. return new FileReader(file); } } // Flush the cached code after the compile; we can read it off disk // if we need it again. @Override public void clearCachedSource() { super.setCode(null); } /** * Store the Charset specification as the string version of the name, * rather than the Charset itself. This allows us to serialize the * SourceFile class. * @param c charset to use when reading the input. */ public void setCharset(Charset c) { inputCharset = c.name(); } /** * Get the Charset specifying how we're supposed to read the file * in off disk and into UTF-16. This is stored as a strong to allow * SourceFile to be serialized. * @return Charset object representing charset to use. */ public Charset getCharset() { return Charset.forName(inputCharset); } } }

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS>(String sourceName) { this.sourceName = sourceName; } public final int getEncodedSourceStart() { return encodedSourceStart; } public final int getEncodedSourceEnd() { return encodedSourceEnd; } public final void setEncodedSourceBounds(int start, int end) { this.encodedSourceStart = start; this.encodedSourceEnd = end; } public final int getBaseLineno() { return baseLineno; } public final void setBaseLineno(int lineno) { // One time action if (lineno < 0 || baseLineno >= 0) Kit.codeBug(); baseLineno = lineno; } public final int getEndLineno() { return endLineno; } public final void setEndLineno(int lineno) { // One time action if (lineno < 0 || endLineno >= 0) Kit.codeBug(); endLineno = lineno; } public final int getFunctionCount() { if (functions == null) { return 0; } return functions.size(); } public final FunctionNode getFunctionNode(int i) { return (FunctionNode)functions.get(i); } public final int addFunction(FunctionNode fnNode) { if (fnNode == null) Kit.codeBug(); if (functions == null) { functions = new ObjArray(); } functions.add(fnNode); return functions.size() - 1; } public final int getRegexpCount() { if (regexps == null) { return 0; } return regexps.size() / 2; } public final String getRegexpString(int index) { return (String)regexps.get(index * 2); } public final String getRegexpFlags(int index) { return (String)regexps.get(index * 2 + 1); } public final int addRegexp(String string, String flags) { if (string == null) Kit.codeBug(); if (regexps == null) { regexps = new ObjArray(); } regexps.add(string); regexps.add(flags); return regexps.size() / 2 - 1; } public final boolean hasParamOrVar

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS>(String name) { return itsVariableNames.has(name); } public final int getParamOrVarIndex(String name) { return itsVariableNames.get(name, -1); } public final String getParamOrVarName(int index) { return (String)itsVariables.get(index); } public final int getParamCount() { return varStart; } public final int getParamAndVarCount() { return itsVariables.size(); } public final String[] getParamAndVarNames() { int N = itsVariables.size(); if (N == 0) { return ScriptRuntime.emptyStrings; } String[] array = new String[N]; itsVariables.toArray(array); return array; } public final boolean[] getParamAndVarConst() { int N = itsVariables.size(); boolean[] array = new boolean[N]; for (int i = 0; i < N; i++) if (itsConst.get(i) != null) array[i] = true; return array; } public final void addParam(String name) { // Check addparam is not called after addLocal if (varStart != itsVariables.size()) Kit.codeBug(); // Allow non-unique parameter names: use the last occurrence (parser // will warn about dups) int index = varStart++; itsVariables.add(name); itsConst.add(null); itsVariableNames.put(name, index); } public static final int NO_DUPLICATE = 1; public static final int DUPLICATE_VAR = 0; public static final int DUPLICATE_PARAMETER = -1; public static final int DUPLICATE_CONST = -2; /** * This function adds a variable to the set of var declarations for a * function (or script). This returns an indicator of a duplicate that * overrides a formal parameter (false if this dups a parameter). * @param name variable name * @return 1 if the name is not any form of duplicate, 0 if it duplicates a * non-parameter, -1 if it duplicates a parameter and -2 if it duplicates a * const. */ public final int

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS> addVar(String name) { int vIndex = itsVariableNames.get(name, -1); if (vIndex != -1) { // There's already a variable or parameter with this name. if (vIndex >= varStart) { Object v = itsConst.get(vIndex); if (v != null) return DUPLICATE_CONST; else return DUPLICATE_VAR; } else return DUPLICATE_PARAMETER; } int index = itsVariables.size(); itsVariables.add(name); itsConst.add(null); itsVariableNames.put(name, index); return NO_DUPLICATE; } public final boolean addConst(String name) { int vIndex = itsVariableNames.get(name, -1); if (vIndex != -1) { // There's already a variable or parameter with this name. return false; } int index = itsVariables.size(); itsVariables.add(name); itsConst.add(name); itsVariableNames.put(name, index); return true; } public final void removeParamOrVar(String name) { int i = itsVariableNames.get(name, -1); if (i != -1) { itsVariables.remove(i); itsVariableNames.remove(name); ObjToIntMap.Iterator iter = itsVariableNames.newIterator(); for (iter.start(); !iter.done(); iter.next()) { int v = iter.getValue(); if (v > i) { iter.setValue(v - 1); } } } } public final Object getCompilerData() { return compilerData; } public final void setCompilerData(Object data) { if (data == null) throw new IllegalArgumentException(); // Can only call once if (compilerData != null) throw new IllegalStateException(); compilerData = data; } private int encodedSourceStart; private int encodedSourceEnd; private String sourceName; private int baseLineno = -1; private int endLineno = -1; private ObjArray functions; private ObjArray regexps; // a list of the formal parameters and local variables private ObjArray itsVariables

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS>.lineLengthThreshold = lineLengthThreshold <= 0 ? Integer.MAX_VALUE : lineLengthThreshold; this.createSrcMap = createSrcMap; this.sourceMapDetailLevel = sourceMapDetailLevel; this.mappings = createSrcMap ? new ArrayDeque<Mapping>() : null; this.allMappings = createSrcMap ? new ArrayList<Mapping>() : null; } /** * Maintains a mapping from a given node to the position * in the source code at which its generated form was * placed. This position is relative only to the current * run of the CodeConsumer and will be normalized * later on by the SourceMap. * * @see SourceMap */ private static class Mapping { Node node; FilePosition start; FilePosition end; } /** * Starts the source mapping for the given * node at the current position. */ @Override void startSourceMapping(Node node) { Preconditions.checkState(sourceMapDetailLevel != null); Preconditions.checkState(node != null); if (createSrcMap && node.getProp(Node.SOURCENAME_PROP) != null && node.getLineno() > 0 && sourceMapDetailLevel.apply(node)) { int line = getCurrentLineIndex(); int index = getCurrentCharIndex(); Preconditions.checkState(line >= 0); Mapping mapping = new Mapping(); mapping.node = node; mapping.start = new FilePosition(line, index); mappings.push(mapping); allMappings.add(mapping); } } /** * Finishes the source mapping for the given * node at the current position. */ @Override void endSourceMapping(Node node) { if (createSrcMap && !mappings.isEmpty() && mappings.peek().node == node) { Mapping mapping = mappings.pop(); int line = getCurrentLineIndex(); int index = getCurrentCharIndex(); Preconditions.checkState(line >= 0); mapping.end = new FilePosition(line, index); } } /** * Generates the source map from the given code consumer, * appending the information it saved to the SourceMap * object given. */ void generateSourceMap(SourceMap map){ if (create

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS>SrcMap) { for (Mapping mapping : allMappings) { map.addMapping(mapping.node, mapping.start, mapping.end); } } } /** * Reports to the code consumer that the given line has been cut at the * given position (i.e. a \n has been inserted there). All mappings in * the source maps after that position will be renormalized as needed. */ void reportLineCut(int lineIndex, int charIndex) { if (createSrcMap) { for (Mapping mapping : allMappings) { mapping.start = convertPosition(mapping.start, lineIndex, charIndex); if (mapping.end != null) { mapping.end = convertPosition(mapping.end, lineIndex, charIndex); } } } } /** * Converts the given position by normalizing it against the insertion * of a newline at the given line and character position. * * @param position The existing position before the newline was inserted. * @param lineIndex The index of the line at which the newline was inserted. * @param characterPosition The position on the line at which the newline * was inserted. * * @return The normalized position. */ private FilePosition convertPosition(FilePosition position, int lineIndex, int characterPosition) { int originalLine = position.getLine(); int originalChar = position.getColumn(); if (originalLine == lineIndex && originalChar >= characterPosition) { // If the position falls on the line itself, then normalize it // if it falls at or after the place the newline was inserted. return new FilePosition( originalLine + 1, originalChar - characterPosition); } else { return position; } } public String getCode() { return code.toString(); } @Override char getLastChar() { return (code.length() > 0) ? code.charAt(code.length() - 1) : '\0'; } protected final int getCurrentCharIndex() { return lineLength; } protected final int getCurrentLineIndex() { return lineIndex; } } static class PrettyCodePrinter extends MappedCodePrinter { // The number of characters after which we insert a line break in the code static

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS> final String INDENT = " "; private int indent = 0; /** * @param lineLengthThreshold The length of a line after which we force * a newline when possible. * @param createSourceMap Whether to generate source map data. * @param sourceMapDetailLevel A filter to control which nodes get mapped * into the source map. */ private PrettyCodePrinter( int lineLengthThreshold, boolean createSourceMap, SourceMap.DetailLevel sourceMapDetailLevel) { super(lineLengthThreshold, createSourceMap, sourceMapDetailLevel); } /** * Appends a string to the code, keeping track of the current line length. */ @Override void append(String str) { // For pretty printing: indent at the beginning of the line if (lineLength == 0) { for (int i = 0; i < indent; i++) { code.append(INDENT); lineLength += INDENT.length(); } } code.append(str); lineLength += str.length(); } /** * Adds a newline to the code, resetting the line length and handling * indenting for pretty printing. */ @Override void startNewLine() { if (lineLength > 0) { code.append('\n'); lineIndex++; lineLength = 0; } } @Override void maybeLineBreak() { maybeCutLine(); } /** * This may start a new line if the current line is longer than the line * length threshold. */ @Override void maybeCutLine() { if (lineLength > lineLengthThreshold) { startNewLine(); } } @Override void endLine() { startNewLine(); } @Override void appendBlockStart() { append(" {"); indent++; } @Override void appendBlockEnd() { endLine(); indent--; append("}"); } @Override void listSeparator() { add(", "); maybeLineBreak(); } @Override void endFunction(boolean statementContext) { super.endFunction(statementContext); if (statementContext) { startNewLine(); } } @Override void beginCaseBody() { super.

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS> being lines longer than the threshold. Since the output is going to be // gzipped, it makes sense to try to make the newlines appear in similar // contexts so that GZIP can encode them for 'free'. // // This version tries to break the lines at 'preferred' places, which are // between the top-level forms. This works because top level forms tend to // be more uniform than arbitary legal contexts. Better compression would // probably require explicit modelling of the gzip algorithm. private final boolean lineBreak; private int lineStartPosition = 0; private int preferredBreakPosition = 0; /** * @param lineBreak break the lines a bit more aggressively * @param lineLengthThreshold The length of a line after which we force * a newline when possible. * @param createSrcMap Whether to gather source position * mapping information when printing. * @param sourceMapDetailLevel A filter to control which nodes get mapped into * the source map. */ private CompactCodePrinter(boolean lineBreak, int lineLengthThreshold, boolean createSrcMap, SourceMap.DetailLevel sourceMapDetailLevel) { super(lineLengthThreshold, createSrcMap, sourceMapDetailLevel); this.lineBreak = lineBreak; } /** * Appends a string to the code, keeping track of the current line length. */ @Override void append(String str) { code.append(str); lineLength += str.length(); } /** * Adds a newline to the code, resetting the line length. */ @Override void startNewLine() { if (lineLength > 0) { code.append('\n'); lineLength = 0; lineIndex++; lineStartPosition = code.length(); } } @Override void maybeLineBreak() { if (lineBreak) { if (sawFunction) { startNewLine(); sawFunction = false; } } // Since we are at a legal line break, can we upgrade the // preferred break position? We prefer to break after a // semicolon rather than before it. int len = code.length(); if (preferredBreakPosition == len - 1) { char ch = code.charAt

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS>(len - 1); if (ch == ';') { preferredBreakPosition = len; } } maybeCutLine(); } /** * This may start a new line if the current line is longer than the line * length threshold. */ @Override void maybeCutLine() { if (lineLength > lineLengthThreshold) { // Use the preferred position provided it will break the line. if (preferredBreakPosition > lineStartPosition && preferredBreakPosition < lineStartPosition + lineLength) { int position = preferredBreakPosition; code.insert(position, '\n'); reportLineCut(lineIndex, position - lineStartPosition); lineIndex++; lineLength -= (position - lineStartPosition); lineStartPosition = position + 1; } else { startNewLine(); } } } @Override void notePreferredLineBreak() { preferredBreakPosition = code.length(); } } static class Builder { private final Node root; private boolean prettyPrint = false; private boolean lineBreak = false; private boolean outputTypes = false; private int lineLengthThreshold = DEFAULT_LINE_LENGTH_THRESHOLD; private SourceMap sourceMap = null; private SourceMap.DetailLevel sourceMapDetailLevel = SourceMap.DetailLevel.ALL; // Specify a charset to use when outputting source code. If null, // then just output ASCII. private Charset outputCharset = null; private boolean tagAsStrict; /** * Sets the root node from which to generate the source code. * @param node The root node. */ Builder(Node node) { root = node; } /** * Sets whether pretty printing should be used. * @param prettyPrint If true, pretty printing will be used. */ Builder setPrettyPrint(boolean prettyPrint) { this.prettyPrint = prettyPrint; return this; } /** * Sets whether line breaking should be done automatically. * @param lineBreak If true, line breaking is done automatically. */ Builder setLineBreak(boolean lineBreak) { this.lineBreak = lineBreak; return this; } /** * Sets whether to output closure-style type annotations. * @param outputTypes If true, outputs closure-style

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS> } private FlowScope caseNameOrGetProp(Node name, FlowScope blindScope, boolean outcome) { JSType type = getTypeIfRefinable(name, blindScope); if (type != null) { JSType restrictedType = type.getRestrictedTypeGivenToBooleanOutcome(outcome); FlowScope informed = blindScope.createChildFlowScope(); declareNameInScope(informed, name, restrictedType); return informed; } return blindScope; } private FlowScope caseTypeOf(Node node, JSType type, String value, boolean resultEqualsValue, FlowScope blindScope) { JSType restrictedType = getRestrictedByTypeOfResult(type, value, resultEqualsValue); if (restrictedType == null) { return blindScope; } FlowScope informed = blindScope.createChildFlowScope(); declareNameInScope(informed, node, restrictedType); return informed; } private FlowScope caseInstanceOf(Node left, Node right, FlowScope blindScope, boolean outcome) { JSType leftType = getTypeIfRefinable(left, blindScope); if (leftType == null) { return blindScope; } JSType rightType = right.getJSType(); ObjectType targetType = typeRegistry.getNativeObjectType(JSTypeNative.UNKNOWN_TYPE); if (rightType instanceof FunctionType) { targetType = (FunctionType) rightType; } Visitor<JSType> visitor; if (outcome) { visitor = new RestrictByTrueInstanceOfResultVisitor(targetType); } else { visitor = new RestrictByFalseInstanceOfResultVisitor(targetType); } JSType restrictedLeftType = leftType.visit(visitor); if (restrictedLeftType != null && !restrictedLeftType.equals(leftType)) { FlowScope informed = blindScope.createChildFlowScope(); declareNameInScope(informed, left, restrictedLeftType); return informed; } return blindScope; } /** * Given 'property in object', ensures that the object has the property in the * informed scope by defining it as a qualified name if the object type lacks * the property and it's not in the blind scope. * @param object The node of the right-

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS> functions that are assigned to variables or // properties // e.g. goog.string.htmlEscape = function(str) { // } // get the function name and see if it's empty Node functionNameNode = n.getFirstChild(); String functionName = functionNameNode.getString(); if (functionName.length() == 0) { if (parent.getType() == Token.ASSIGN) { // this is an assignment to a property, generally either a // static function or a prototype function // e.g. goog.string.htmlEscape = function() { } or // goog.structs.Map.prototype.getCount = function() { } Node lhs = parent.getFirstChild(); String name = namer.getName(lhs); namer.setFunctionName(name, n); } else if (parent.getType() == Token.NAME) { // this is an assignment to a variable // e.g. var handler = function() {} String name = namer.getName(parent); namer.setFunctionName(name, n); } } break; case Token.ASSIGN: // this handles functions that are assigned to a prototype through // an object literal // e.g. BuzzApp.prototype = { // Start : function() { } // } Node lhs = n.getFirstChild(); Node rhs = lhs.getNext(); if (rhs.getType() == Token.OBJECTLIT) { nameObjectLiteralMethods(rhs, namer.getName(lhs)); } } } private void nameObjectLiteralMethods(Node objectLiteral, String context) { for (Node keyNode = objectLiteral.getFirstChild(); keyNode != null; keyNode = keyNode.getNext()) { Node valueNode = keyNode.getFirstChild(); // Object literal keys may be STRING, GET, SET or NUMBER. Numbers are // skipped because name tokens may not start with a number. Get and Set // are skipped because they can not be named. if (keyNode.getType() == Token.STRING) { // concatenate the context and key name to get a new qualified name. String name = namer.getCombinedName(context, namer.getName(keyNode)); int type = valueNode.getType(); if (type ==

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS>/* * Copyright 2007 The Closure Compiler Authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.google.javascript.jscomp; import com.google.javascript.rhino.Node; import java.util.regex.Pattern; /** * This describes the Google-specific JavaScript coding conventions. * Within Google, variable names are semantically significant. * */ public class GoogleCodingConvention extends ClosureCodingConvention { private static final long serialVersionUID = 1L; private static final String OPTIONAL_ARG_PREFIX = "opt_"; private static final String VAR_ARGS_NAME = "var_args"; private static final Pattern ENUM_KEY_PATTERN = Pattern.compile("[A-Z0-9][A-Z0-9_]*"); /** * {@inheritDoc} * * <p>This enforces the Google const name convention, that the first character * after the last $ must be an upper-case letter and all subsequent letters * must be upper case. The name must be at least 2 characters long. * * <p>Examples: * <pre> * aaa Not constant - lower-case letters in the name * A Not constant - too short * goog$A Constant - letters after the $ are upper-case. * AA17 Constant - digits can appear after the first letter * goog$7A Not constant - first character after the $ must be * upper case. * $A Constant - doesn't have to be anything in front of the $ * </pre> */ @Override public boolean isConstant(String name) { if (name.length() <= 1)

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS> { return false; } // In compiled code, '$' is often a namespace delimiter. To allow inlining // of namespaced constants, we strip off any namespaces here. int pos = name.lastIndexOf('$'); if (pos >= 0) { name = name.substring(pos + 1); if (name.length() == 0) { return false; } } return isConstantKey(name); } @Override public boolean isConstantKey(String name) { if (name.isEmpty() || !Character.isUpperCase(name.charAt(0))) { return false; } // hack way of checking that there aren't any lower-case letters return name.toUpperCase().equals(name); } /** * {@inheritDoc} * * <p>This enforces Google's convention about enum key names. They must match * the regular expression {@code [A-Z0-9][A-Z0-9_]*}. * * <p>Examples: * <ul> * <li>A</li> * <li>213</li> * <li>FOO_BAR</li> * </ul> */ @Override public boolean isValidEnumKey(String key) { return ENUM_KEY_PATTERN.matcher(key).matches(); } /** * {@inheritDoc} * * <p>In Google code, parameter names beginning with {@code opt_} are * treated as optional arguments. */ @Override public boolean isOptionalParameter(Node parameter) { return parameter.getString().startsWith(OPTIONAL_ARG_PREFIX); } @Override public boolean isVarArgsParameter(Node parameter) { return VAR_ARGS_NAME.equals(parameter.getString()); } /** * {@inheritDoc} * * <p>In Google code, any global name starting with an underscore is * considered exported. */ @Override public boolean isExported(String name, boolean local) { return super.isExported(name, local) || (!local && name.startsWith("_")); } /** * {@inheritDoc} * * <p>In Google code, private names end with an underscore, and exported * names are never considered private (see {@link

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS>Token.STRING, key.getType()); assertEquals(3, key.getLineno()); assertEquals(10, key.getCharno()); Node value = key.getFirstChild(); assertEquals(Token.NUMBER, value.getType()); assertEquals(3, value.getLineno()); assertEquals(12, value.getCharno()); key = key.getNext(); assertEquals(Token.STRING, key.getType()); assertEquals(4, key.getLineno()); assertEquals(1, key.getCharno()); value = key.getFirstChild(); assertEquals(Token.NUMBER, value.getType()); assertEquals(4, value.getLineno()); assertEquals(4, value.getCharno()); } public void testLinenoCharnoAdd() throws Exception { testLinenoCharnoBinop("+"); } public void testLinenoCharnoSub() throws Exception { testLinenoCharnoBinop("-"); } public void testLinenoCharnoMul() throws Exception { testLinenoCharnoBinop("*"); } public void testLinenoCharnoDiv() throws Exception { testLinenoCharnoBinop("/"); } public void testLinenoCharnoMod() throws Exception { testLinenoCharnoBinop("%"); } public void testLinenoCharnoShift() throws Exception { testLinenoCharnoBinop("<<"); } public void testLinenoCharnoBinaryAnd() throws Exception { testLinenoCharnoBinop("&"); } public void testLinenoCharnoAnd() throws Exception { testLinenoCharnoBinop("&&"); } public void testLinenoCharnoBinaryOr() throws Exception { testLinenoCharnoBinop("|"); } public void testLinenoCharnoOr() throws Exception { testLinenoCharnoBinop("||"); } public void testLinenoCharnoLt() throws Exception { testLinenoCharnoBinop("<"); } public void testLinenoCharnoLe() throws Exception { testLinenoCharnoBinop("<="); } public void testLinenoCharnoGt() throws Exception { testLinenoCharnoBinop(">"); } public void testLinenoCharnoGe() throws Exception { testLinenoCharnoBinop(">=");

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS> } private void testLinenoCharnoBinop(String binop) { Node op = parse("var a = 89 " + binop + " 76").getFirstChild(). getFirstChild().getFirstChild(); assertEquals(1, op.getLineno()); assertEquals(11, op.getCharno()); } public void testJSDocAttachment1() { Node varNode = parse("/** @type number */var a;").getFirstChild(); // VAR assertEquals(Token.VAR, varNode.getType()); JSDocInfo info = varNode.getJSDocInfo(); assertNotNull(info); assertTypeEquals(NUMBER_TYPE, info.getType()); // NAME Node nameNode = varNode.getFirstChild(); assertEquals(Token.NAME, nameNode.getType()); assertNull(nameNode.getJSDocInfo()); } public void testJSDocAttachment2() { Node varNode = parse("/** @type number */var a,b;").getFirstChild(); // VAR assertEquals(Token.VAR, varNode.getType()); JSDocInfo info = varNode.getJSDocInfo(); assertNotNull(info); assertTypeEquals(NUMBER_TYPE, info.getType()); // First NAME Node nameNode1 = varNode.getFirstChild(); assertEquals(Token.NAME, nameNode1.getType()); assertNull(nameNode1.getJSDocInfo()); // Second NAME Node nameNode2 = nameNode1.getNext(); assertEquals(Token.NAME, nameNode2.getType()); assertNull(nameNode2.getJSDocInfo()); } public void testJSDocAttachment3() { Node assignNode = parse( "/** @type number */goog.FOO = 5;").getFirstChild().getFirstChild(); // ASSIGN assertEquals(Token.ASSIGN, assignNode.getType()); JSDocInfo info = assignNode.getJSDocInfo(); assertNotNull(info); assertTypeEquals(NUMBER_TYPE, info.getType()); } public void testJSDocAttachment4() { Node varNode = parse( "var a, /** @define {number} */b = 5;").getFirstChild(); // ASSIGN assertEquals(Token.VAR, varNode.getType());

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS> assertNull(varNode.getJSDocInfo()); // a Node a = varNode.getFirstChild(); assertNull(a.getJSDocInfo()); // b Node b = a.getNext(); JSDocInfo info = b.getJSDocInfo(); assertNotNull(info); assertTrue(info.isDefine()); assertTypeEquals(NUMBER_TYPE, info.getType()); } public void testJSDocAttachment5() { Node varNode = parse( "var /** @type number */a, /** @define {number} */b = 5;") .getFirstChild(); // ASSIGN assertEquals(Token.VAR, varNode.getType()); assertNull(varNode.getJSDocInfo()); // a Node a = varNode.getFirstChild(); assertNotNull(a.getJSDocInfo()); JSDocInfo info = a.getJSDocInfo(); assertNotNull(info); assertFalse(info.isDefine()); assertTypeEquals(NUMBER_TYPE, info.getType()); // b Node b = a.getNext(); info = b.getJSDocInfo(); assertNotNull(info); assertTrue(info.isDefine()); assertTypeEquals(NUMBER_TYPE, info.getType()); } /** * Tests that a JSDoc comment in an unexpected place of the code does not * propagate to following code due to {@link JSDocInfo} aggregation. */ public void testJSDocAttachment6() throws Exception { Node functionNode = parse( "var a = /** @param {number} index */5;" + "/** @return boolean */function f(index){}") .getFirstChild().getNext(); assertEquals(Token.FUNCTION, functionNode.getType()); JSDocInfo info = functionNode.getJSDocInfo(); assertNotNull(info); assertFalse(info.hasParameter("index")); assertTrue(info.hasReturnType()); assertTypeEquals(UNKNOWN_TYPE, info.getReturnType()); } public void testJSDocAttachment7() { Node varNode = parse("/** */var a;").getFirstChild(); // VAR assertEquals(Token.VAR, varNode.getType()); // NAME Node nameNode = varNode.getFirstChild(); assertEquals(Token.NAME, nameNode.getType());

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS> assertNull(nameNode.getJSDocInfo()); } public void testJSDocAttachment8() { Node varNode = parse("/** x */var a;").getFirstChild(); // VAR assertEquals(Token.VAR, varNode.getType()); // NAME Node nameNode = varNode.getFirstChild(); assertEquals(Token.NAME, nameNode.getType()); assertNull(nameNode.getJSDocInfo()); } public void testJSDocAttachment9() { Node varNode = parse("/** \n x */var a;").getFirstChild(); // VAR assertEquals(Token.VAR, varNode.getType()); // NAME Node nameNode = varNode.getFirstChild(); assertEquals(Token.NAME, nameNode.getType()); assertNull(nameNode.getJSDocInfo()); } public void testJSDocAttachment10() { Node varNode = parse("/** x\n */var a;").getFirstChild(); // VAR assertEquals(Token.VAR, varNode.getType()); // NAME Node nameNode = varNode.getFirstChild(); assertEquals(Token.NAME, nameNode.getType()); assertNull(nameNode.getJSDocInfo()); } public void testJSDocAttachment11() { Node varNode = parse("/** @type {{x : number, 'y' : string, z}} */var a;") .getFirstChild(); // VAR assertEquals(Token.VAR, varNode.getType()); JSDocInfo info = varNode.getJSDocInfo(); assertNotNull(info); assertTypeEquals(createRecordTypeBuilder(). addProperty("x", NUMBER_TYPE, null). addProperty("y", STRING_TYPE, null). addProperty("z", UNKNOWN_TYPE, null). build(), info.getType()); // NAME Node nameNode = varNode.getFirstChild(); assertEquals(Token.NAME, nameNode.getType()); assertNull(nameNode.getJSDocInfo()); } public void testJSDocAttachment12() { Node varNode = parse("var a = {/** @type {Object} */ b: c};") .getFirstChild(); Node objectLitNode = varNode.getFirstChild().getFirstChild(); assertEquals(Token.OBJECTLIT, objectLit

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS>Node<N, E> dNode2 = getNodeOrFail(n2); for (DiGraphEdge<N, E> outEdge : dNode1.getOutEdges()) { if (outEdge.getDestination() == dNode2) { return outEdge; } } for (DiGraphEdge<N, E> outEdge : dNode2.getOutEdges()) { if (outEdge.getDestination() == dNode1) { return outEdge; } } return null; } @Override public GraphNode<N, E> createNode(N value) { return createDirectedGraphNode(value); } @Override public List<DiGraphEdge<N, E>> getDirectedGraphEdges(N n1, N n2) { DiGraphNode<N, E> dNode1 = getNodeOrFail(n1); DiGraphNode<N, E> dNode2 = getNodeOrFail(n2); List<DiGraphEdge<N, E>> edges = Lists.newArrayList(); for (DiGraphEdge<N, E> outEdge : dNode1.getOutEdges()) { if (outEdge.getDestination() == dNode2) { edges.add(outEdge); } } return edges; } @Override public boolean isConnectedInDirection(N n1, N n2) { return isConnectedInDirection(n1, Predicates.<E>alwaysTrue(), n2); } @Override public boolean isConnectedInDirection(N n1, E edgeValue, N n2) { return isConnectedInDirection(n1, Predicates.equalTo(edgeValue), n2); } private boolean isConnectedInDirection(N n1, Predicate<E> edgeMatcher, N n2) { // Verify the nodes. DiGraphNode<N, E> dNode1 = getNodeOrFail(n1); DiGraphNode<N, E> dNode2 = getNodeOrFail(n2); for (DiGraphEdge<N, E> outEdge : dNode1.getOutEdges()) { if (outEdge.getDestination() == dNode2 && edgeMatcher.apply(outEdge.getValue())) { return true; } } return false; }

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS> } @Override public boolean isDirected() { return true; } @Override public Collection<GraphNode<N, E>> getNodes() { return Collections.<GraphNode<N, E>>unmodifiableCollection(nodes.values()); } @Override public List<GraphNode<N, E>> getNeighborNodes(N value) { DiGraphNode<N, E> node = getDirectedGraphNode(value); return getNeighborNodes(node); } public List<GraphNode<N, E>> getNeighborNodes(DiGraphNode<N, E> node) { List<GraphNode<N, E>> result = Lists.newArrayList(); for (Iterator<GraphNode<N, E>> i = ((LinkedDirectedGraphNode<N, E>) node).neighborIterator();i.hasNext();) { result.add(i.next()); } return result; } @Override public Iterator<GraphNode<N, E>> getNeighborNodesIterator(N value) { LinkedDirectedGraphNode<N, E> node = nodes.get(value); Preconditions.checkNotNull(node); return node.neighborIterator(); } @Override public List<GraphEdge<N, E>> getEdges() { List<GraphEdge<N, E>> result = Lists.newArrayList(); for (DiGraphNode<N, E> node : nodes.values()) { for (DiGraphEdge<N, E> edge : node.getOutEdges()) { result.add(edge); } } return Collections.unmodifiableList(result); } @Override public int getNodeDegree(N value) { DiGraphNode<N, E> node = getNodeOrFail(value); return node.getInEdges().size() + node.getOutEdges().size(); } /** * A directed graph node that stores outgoing edges and incoming edges as an * list within the node itself. */ static class LinkedDirectedGraphNode<N, E> implements DiGraphNode<N, E>, GraphvizNode { List<DiGraphEdge<N, E>> inEdgeList = Lists.newArrayList(); List<DiGraphEdge<N, E>> outEdgeList = Lists.newArrayList(); protected final N value; /** * Constructor

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS> * * @param nodeValue Node's value. */ LinkedDirectedGraphNode(N nodeValue) { this.value = nodeValue; } @Override public N getValue() { return value; } @Override public <A extends Annotation> A getAnnotation() { throw new UnsupportedOperationException( "Graph initialized with node annotations turned off"); } @Override public void setAnnotation(Annotation data) { throw new UnsupportedOperationException( "Graph initialized with node annotations turned off"); } @Override public String getColor() { return "white"; } @Override public String getId() { return "LDN" + hashCode(); } @Override public String getLabel() { return value != null ? value.toString() : "null"; } @Override public String toString() { return getLabel(); } @Override public List<DiGraphEdge<N, E>> getInEdges() { return inEdgeList; } @Override public List<DiGraphEdge<N, E>> getOutEdges() { return outEdgeList; } private Iterator<GraphNode<N, E>> neighborIterator() { return new NeighborIterator(); } private class NeighborIterator implements Iterator<GraphNode<N, E>> { private final Iterator<DiGraphEdge<N, E>> in = inEdgeList.iterator(); private final Iterator<DiGraphEdge<N, E>> out = outEdgeList.iterator(); @Override public boolean hasNext() { return in.hasNext() || out.hasNext(); } @Override public GraphNode<N, E> next() { boolean isOut = !in.hasNext(); Iterator<DiGraphEdge<N, E>> curIterator = isOut ? out : in; DiGraphEdge<N, E> s = curIterator.next(); return isOut ? s.getDestination() : s.getSource(); } @Override public void remove() { throw new UnsupportedOperationException("Remove not supported."); } } } /** * A directed graph node with annotations. */ static class AnnotatedLinkedDirectedGraphNode<N, E> extends LinkedDirectedGraphNode<N, E> { protected Annotation annotation; /** * @

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS>param nodeValue Node's value. */ AnnotatedLinkedDirectedGraphNode(N nodeValue) { super(nodeValue); } @SuppressWarnings("unchecked") @Override public <A extends Annotation> A getAnnotation() { return (A) annotation; } @Override public void setAnnotation(Annotation data) { annotation = data; } } /** * A directed graph edge that stores the source and destination nodes at each * edge. */ static class LinkedDirectedGraphEdge<N, E> implements DiGraphEdge<N, E>, GraphvizEdge { private DiGraphNode<N, E> sourceNode; private DiGraphNode<N, E> destNode; protected final E value; /** * Constructor. * * @param edgeValue Edge Value. */ LinkedDirectedGraphEdge(DiGraphNode<N, E> sourceNode, E edgeValue, DiGraphNode<N, E> destNode) { this.value = edgeValue; this.sourceNode = sourceNode; this.destNode = destNode; } @Override public DiGraphNode<N, E> getSource() { return sourceNode; } @Override public DiGraphNode<N, E> getDestination() { return destNode; } @Override public void setDestination(DiGraphNode<N, E> node) { destNode = node; } @Override public void setSource(DiGraphNode<N, E> node) { sourceNode = node; } @Override public E getValue() { return value; } @Override public <A extends Annotation> A getAnnotation() { throw new UnsupportedOperationException( "Graph initialized with edge annotations turned off"); } @Override public void setAnnotation(Annotation data) { throw new UnsupportedOperationException( "Graph initialized with edge annotations turned off"); } @Override public String getColor() { return "black"; } @Override public String getLabel() { return value != null ? value.toString() : "null"; } @Override public String getNode1Id() { return ((LinkedDirectedGraphNode<N, E>) sourceNode).getId(); } @Override public String getNode

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS>2Id() { return ((LinkedDirectedGraphNode<N, E>) destNode).getId(); } @Override public String toString() { return sourceNode.toString() + " -> " + destNode.toString(); } @Override public GraphNode<N, E> getNodeA() { return sourceNode; } @Override public GraphNode<N, E> getNodeB() { return destNode; } } /** * A directed graph edge that stores the source and destination nodes at each * edge. */ static class AnnotatedLinkedDirectedGraphEdge<N, E> extends LinkedDirectedGraphEdge<N, E> { protected Annotation annotation; /** * Constructor. * * @param edgeValue Edge Value. */ AnnotatedLinkedDirectedGraphEdge(DiGraphNode<N, E> sourceNode, E edgeValue, DiGraphNode<N, E> destNode) { super(sourceNode, edgeValue, destNode); } @SuppressWarnings("unchecked") @Override public <A extends Annotation> A getAnnotation() { return (A) annotation; } @Override public void setAnnotation(Annotation data) { annotation = data; } } }

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS> implements NodeTraversal.Callback { private final CodingConvention convention; PrepareAnnotations(AbstractCompiler compiler) { this.convention = compiler.getCodingConvention(); } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.OBJECTLIT) { normalizeObjectLiteralAnnotations(n); } return true; } @Override public void visit(NodeTraversal t, Node n, Node parent) { switch (n.getType()) { case Token.CALL: annotateCalls(n); break; case Token.FUNCTION: annotateFunctions(n, parent); annotateDispatchers(n, parent); break; } } private void normalizeObjectLiteralAnnotations(Node objlit) { Preconditions.checkState(objlit.getType() == Token.OBJECTLIT); for (Node key = objlit.getFirstChild(); key != null; key = key.getNext()) { Node value = key.getFirstChild(); normalizeObjectLiteralKeyAnnotations(objlit, key, value); } } /** * There are two types of calls we are interested in calls without explicit * "this" values (what we are call "free" calls) and direct call to eval. */ private void annotateCalls(Node n) { Preconditions.checkState(n.getType() == Token.CALL); // Keep track of of the "this" context of a call. A call without an // explicit "this" is a free call. Node first = n.getFirstChild(); if (!NodeUtil.isGet(first)) { n.putBooleanProp(Node.FREE_CALL, true); } // Keep track of the context in which eval is called. It is important // to distinguish between "(0, eval)()" and "eval()". if (first.getType() == Token.NAME && "eval".equals(first.getString())) { first.putBooleanProp(Node.DIRECT_EVAL, true); } } /** * Translate dispatcher info into the property expected node. */ private void annotateDispatchers(Node n, Node parent) { Preconditions.checkState(n.getType() == Token.FUNCTION); if (parent.getJSDocInfo() != null &&

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS> parent.getJSDocInfo().isJavaDispatch()) { if (parent.getType() == Token.ASSIGN) { Preconditions.checkState(parent.getLastChild() == n); n.putBooleanProp(Node.IS_DISPATCHER, true); } } } /** * In the AST that Rhino gives us, it needs to make a distinction * between jsdoc on the object literal node and jsdoc on the object literal * value. For example, * <pre> * var x = { * / JSDOC / * a: 'b', * c: / JSDOC / 'd' * }; * </pre> * * But in few narrow cases (in particular, function literals), it's * a lot easier for us if the doc is attached to the value. */ private void normalizeObjectLiteralKeyAnnotations( Node objlit, Node key, Node value) { Preconditions.checkState(objlit.getType() == Token.OBJECTLIT); if (key.getJSDocInfo() != null && value.getType() == Token.FUNCTION) { value.setJSDocInfo(key.getJSDocInfo()); } } /** * Annotate optional and var_arg function parameters. */ private void annotateFunctions(Node n, Node parent) { JSDocInfo fnInfo = NodeUtil.getFunctionInfo(n); // Compute which function parameters are optional and // which are var_args. Node args = n.getFirstChild().getNext(); for (Node arg = args.getFirstChild(); arg != null; arg = arg.getNext()) { String argName = arg.getString(); JSTypeExpression typeExpr = fnInfo == null ? null : fnInfo.getParameterType(argName); if (convention.isOptionalParameter(arg) || typeExpr != null && typeExpr.isOptionalArg()) { arg.putBooleanProp(Node.IS_OPTIONAL_PARAM, true); } if (convention.isVarArgsParameter(arg) || typeExpr != null && typeExpr.isVarArgs()) { arg.putBooleanProp(Node.IS_VAR_ARGS_PARAM, true); } } } } }

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS> warning message. * @param ownerType The type of the owner of the property, for use * in the warning message. */ void expectCanOverride(NodeTraversal t, Node n, JSType overridingType, JSType hiddenType, String propertyName, JSType ownerType) { if (!overridingType.canAssignTo(hiddenType)) { registerMismatch(overridingType, hiddenType); if (shouldReport) { compiler.report( t.makeError(n, HIDDEN_PROPERTY_MISMATCH, propertyName, ownerType.toString(), hiddenType.toString(), overridingType.toString())); } } } /** * Expect that the first type is the direct superclass of the second type. * * @param t The node traversal. * @param n The node where warnings should point to. * @param superObject The expected super instance type. * @param subObject The sub instance type. */ void expectSuperType(NodeTraversal t, Node n, ObjectType superObject, ObjectType subObject) { FunctionType subCtor = subObject.getConstructor(); ObjectType declaredSuper = subObject.getImplicitPrototype().getImplicitPrototype(); if (!declaredSuper.equals(superObject)) { if (declaredSuper.equals(getNativeType(OBJECT_TYPE))) { if (shouldReport) { compiler.report( t.makeError(n, MISSING_EXTENDS_TAG_WARNING, subObject.toString())); } registerMismatch(superObject, declaredSuper); } else { mismatch(t.getSourceName(), n, "mismatch in declaration of superclass type", superObject, declaredSuper); } // Correct the super type. if (!subCtor.hasCachedValues()) { subCtor.setPrototypeBasedOn(superObject); } } } /** * Expect that the first type can be cast to the second type. The first type * should be either a subtype or supertype of the second. * * @param t The node traversal. * @param n The node where warnings should point. * @param type The type being cast from. * @param castType The type being cast to. */ void expectCanCast(NodeTraversal t, Node n, JSType type, JSType cast

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS>Type) { castType = castType.restrictByNotNullOrUndefined(); type = type.restrictByNotNullOrUndefined(); if (!type.canAssignTo(castType) && !castType.canAssignTo(type)) { if (shouldReport) { compiler.report( t.makeError(n, INVALID_CAST, castType.toString(), type.toString())); } registerMismatch(type, castType); } } /** * Expect that the given variable has not been declared with a type. * * @param sourceName The name of the source file we're in. * @param n The node where warnings should point to. * @param parent The parent of {@code n}. * @param var The variable that we're checking. * @param variableName The name of the variable. * @param newType The type being applied to the variable. Mostly just here * for the benefit of the warning. */ void expectUndeclaredVariable(String sourceName, Node n, Node parent, Var var, String variableName, JSType newType) { boolean allowDupe = false; if (n.getType() == Token.GETPROP || NodeUtil.isObjectLitKey(n, parent)) { JSDocInfo info = n.getJSDocInfo(); if (info == null) { info = parent.getJSDocInfo(); } allowDupe = info != null && info.getSuppressions().contains("duplicate"); } JSType varType = var.getType(); // Only report duplicate declarations that have types. Other duplicates // will be reported by the syntactic scope creator later in the // compilation process. if (varType != null && varType != typeRegistry.getNativeType(UNKNOWN_TYPE) && newType != null && newType != typeRegistry.getNativeType(UNKNOWN_TYPE)) { // If there are two typed declarations of the same variable, that // is an error and the second declaration is ignored, except in the // case of native types. A null input type means that the declaration // was made in TypedScopeCreator#createInitialScope and is a // native type. if (var.input == null) { n.setJSType(varType);

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS> if (parent.getType() == Token.VAR) { if (n.getFirstChild() != null) { n.getFirstChild().setJSType(varType); } } else { Preconditions.checkState(parent.getType() == Token.FUNCTION); parent.setJSType(varType); } } else { // Always warn about duplicates if the overridden type does not // match the original type. // // If the types match, suppress the warning iff there was a @suppress // tag, or if the original declaration was a stub. if (!(allowDupe || var.getParentNode().getType() == Token.EXPR_RESULT) || !newType.equals(varType)) { if (shouldReport) { compiler.report( JSError.make(sourceName, n, DUP_VAR_DECLARATION, variableName, newType.toString(), var.getInputName(), String.valueOf(var.nameNode.getLineno()), varType.toString())); } } } } } /** * Expect that all properties on interfaces that this type implements are * implemented and correctly typed. */ void expectAllInterfaceProperties(NodeTraversal t, Node n, FunctionType type) { ObjectType instance = type.getInstanceType(); for (ObjectType implemented : type.getAllImplementedInterfaces()) { if (implemented.getImplicitPrototype() != null) { for (String prop : implemented.getImplicitPrototype().getOwnPropertyNames()) { expectInterfaceProperty(t, n, instance, implemented, prop); } } } } /** * Expect that the peroperty in an interface that this type implements is * implemented and correctly typed. */ private void expectInterfaceProperty(NodeTraversal t, Node n, ObjectType instance, ObjectType implementedInterface, String prop) { if (!instance.hasProperty(prop)) { // Not implemented String sourceName = (String) n.getProp(Node.SOURCENAME_PROP); sourceName = sourceName == null ? "" : sourceName; if (shouldReport) { compiler.report(JSError.make(sourceName, n, INTERFACE_METHOD_NOT_IMPLEMENTED, prop, implementedInterface.toString(), instance.toString())); } registerMismatch(instance, implemented

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS>Interface); } else { JSType found = instance.getPropertyType(prop); JSType required = implementedInterface.getImplicitPrototype().getPropertyType(prop); found = found.restrictByNotNullOrUndefined(); required = required.restrictByNotNullOrUndefined(); if (!found.canAssignTo(required)) { // Implemented, but not correctly typed if (shouldReport) { FunctionType constructor = implementedInterface.toObjectType().getConstructor(); compiler.report(t.makeError(n, HIDDEN_INTERFACE_PROPERTY_MISMATCH, prop, constructor.getTopMostDefiningType(prop).toString(), required.toString(), found.toString())); } registerMismatch(found, required); } } } /** * Report a type mismatch */ private void mismatch(NodeTraversal t, Node n, String msg, JSType found, JSType required) { mismatch(t.getSourceName(), n, msg, found, required); } private void mismatch(NodeTraversal t, Node n, String msg, JSType found, JSTypeNative required) { mismatch(t, n, msg, found, getNativeType(required)); } private void mismatch(String sourceName, Node n, String msg, JSType found, JSType required) { registerMismatch(found, required); if (shouldReport) { compiler.report( JSError.make(sourceName, n, TYPE_MISMATCH_WARNING, formatFoundRequired(msg, found, required))); } } private void registerMismatch(JSType found, JSType required) { // Don't register a mismatch for differences in null or undefined or if the // code didn't downcast. found = found.restrictByNotNullOrUndefined(); required = required.restrictByNotNullOrUndefined(); if (found.canAssignTo(required) || required.canAssignTo(found)) { return; } mismatches.add(new TypeMismatch(found, required)); if (found instanceof FunctionType && required instanceof FunctionType) { FunctionType fnTypeA = ((FunctionType) found); FunctionType fnTypeB = ((FunctionType) required); Iterator<Node> paramItA = fnTypeA.getParameters().iterator(); Iterator<Node> paramItB = fnTypeB

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS>.getParameters().iterator(); while (paramItA.hasNext() && paramItB.hasNext()) { registerIfMismatch(paramItA.next().getJSType(), paramItB.next().getJSType()); } registerIfMismatch(fnTypeA.getReturnType(), fnTypeB.getReturnType()); } } private void registerIfMismatch(JSType found, JSType required) { if (found != null && required != null && !found.canAssignTo(required)) { registerMismatch(found, required); } } /** * Formats a found/required error message. */ private String formatFoundRequired(String description, JSType found, JSType required) { return MessageFormat.format(FOUND_REQUIRED, description, found, required); } /** * Given a node, get a human-readable name for the type of that node so * that will be easy for the programmer to find the original declaration. * * For example, if SubFoo's property "bar" might have the human-readable * name "Foo.prototype.bar". * * @param n The node. * @param dereference If true, the type of the node will be dereferenced * to an Object type, if possible. */ String getReadableJSTypeName(Node n, boolean dereference) { // If we're analyzing a GETPROP, the property may be inherited by the // prototype chain. So climb the prototype chain and find out where // the property was originally defined. if (n.getType() == Token.GETPROP) { ObjectType objectType = getJSType(n.getFirstChild()).dereference(); if (objectType != null) { String propName = n.getLastChild().getString(); while (objectType != null && !objectType.hasOwnProperty(propName)) { objectType = objectType.getImplicitPrototype(); } // Don't show complex function names or anonymous types. // Instead, try to get a human-readable type name. if (objectType != null && (objectType.getConstructor() != null || objectType.isFunctionPrototypeType())) { return objectType.toString() + "." + propName; } } } JSType type = getJSType(n);

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS> if (dereference) { ObjectType dereferenced = type.dereference(); if (dereferenced != null) { type = dereferenced; } } String qualifiedName = n.getQualifiedName(); if (type.isFunctionPrototypeType() || (type.toObjectType() != null && type.toObjectType().getConstructor() != null)) { return type.toString(); } else if (qualifiedName != null) { return qualifiedName; } else if (type instanceof FunctionType) { // Don't show complex function names. return "function"; } else { return type.toString(); } } /** * This method gets the JSType from the Node argument and verifies that it is * present. */ private JSType getJSType(Node n) { JSType jsType = n.getJSType(); if (jsType == null) { // TODO(user): This branch indicates a compiler bug, not worthy of // halting the compilation but we should log this and analyze to track // down why it happens. This is not critical and will be resolved over // time as the type checker is extended. return getNativeType(UNKNOWN_TYPE); } else { return jsType; } } private JSType getNativeType(JSTypeNative typeId) { return typeRegistry.getNativeType(typeId); } /** * Signals that the first type and the second type have been * used interchangeably. * * Type-based optimizations should take this into account * so that they don't wreck code with type warnings. */ static class TypeMismatch { final JSType typeA; final JSType typeB; /** * It's the responsibility of the class that creates the * {@code TypeMismatch} to ensure that {@code a} and {@code b} are * non-matching types. */ TypeMismatch(JSType a, JSType b) { this.typeA = a; this.typeB = b; } @Override public boolean equals(Object object) { if (object instanceof TypeMismatch) { TypeMismatch that = (TypeMismatch) object; return (that.typeA.equals(this.typeA) && that.typeB

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS>.equals(this.typeB)) || (that.typeB.equals(this.typeA) && that.typeA.equals(this.typeB)); } return false; } @Override public int hashCode() { return Objects.hashCode(typeA, typeB); } @Override public String toString() { return "(" + typeA + ", " + typeB + ")"; } } }

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS>/* * Copyright 2009 The Closure Compiler Authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.google.javascript.jscomp.parsing; import com.google.common.base.Preconditions; import com.google.javascript.jscomp.mozilla.rhino.ScriptRuntime; /** * This class implements the scanner for JsDoc strings. * * It is heavily based on Rhino's TokenStream. * */ class JsDocTokenStream { /* * For chars - because we need something out-of-range * to check. (And checking EOF by exception is annoying.) * Note distinction from EOF token type! */ private final static int EOF_CHAR = -1; JsDocTokenStream(String sourceString) { this(sourceString, 0); } JsDocTokenStream(String sourceString, int lineno) { this(sourceString, lineno, 0); } JsDocTokenStream(String sourceString, int lineno, int initCharno) { Preconditions.checkNotNull(sourceString); this.lineno = lineno; this.sourceString = sourceString; this.sourceEnd = sourceString.length(); this.sourceCursor = this.cursor = 0; this.initLineno = lineno; this.initCharno = initCharno; } /** * Tokenizes JSDoc comments. */ @SuppressWarnings("fallthrough") final JsDocToken getJsDocToken() { int c; stringBufferTop = 0; for (;;) { // eat white spaces for (;;) { charno = -1; c = getChar(); if (c == EOF_CHAR) { return

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS>c2)) { ungetChar(c2); this.string = getStringFromBuffer(); stringBufferTop = 0; return JsDocToken.STRING; } else { do { c1 = c2; c2 = getChar(); if (c1 == '.' && c2 == '<') { ungetChar(c2); ungetChar(c1); this.string = getStringFromBuffer(); stringBufferTop = 0; return JsDocToken.STRING; } else { if (isJSDocString(c2)) { addToString(c1); } else { ungetChar(c2); addToString(c1); this.string = getStringFromBuffer(); stringBufferTop = 0; return JsDocToken.STRING; } } } while (true); } } } } } /** * Gets the remaining JSDoc line without the {@link JsDocToken#EOL}, * {@link JsDocToken#EOF} or {@link JsDocToken#EOC}. */ @SuppressWarnings("fallthrough") String getRemainingJSDocLine() { int c; for (;;) { c = getChar(); switch (c) { case '*': if (peekChar() != '/') { addToString(c); break; } // fall through case EOF_CHAR: case '\n': ungetChar(c); this.string = getStringFromBuffer(); stringBufferTop = 0; return this.string; default: addToString(c); break; } } } final int getLineno() { return lineno; } final int getCharno() { return lineno == initLineno? initCharno + charno : charno; } final String getString() { return string; } final boolean eof() { return hitEOF; } private String getStringFromBuffer() { tokenEnd = cursor; return new String(stringBuffer, 0, stringBufferTop); } private void addToString(int c) { int N = stringBufferTop; if (N == stringBuffer.length) { char[] tmp = new char[stringBuffer.length * 2]; System.arraycopy

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS> == Character.FORMAT; } /** * Allows the JSDocParser to update the character offset * so that getCharno() returns a valid character position. */ void update() { charno = getOffset(); } private int peekChar() { int c = getChar(); ungetChar(c); return c; } protected int getChar() { if (ungetCursor != 0) { cursor++; --ungetCursor; if (charno == -1) { charno = getOffset(); } return ungetBuffer[ungetCursor]; } for(;;) { int c; if (sourceCursor == sourceEnd) { hitEOF = true; if (charno == -1) { charno = getOffset(); } return EOF_CHAR; } cursor++; c = sourceString.charAt(sourceCursor++); if (lineEndChar >= 0) { if (lineEndChar == '\r' && c == '\n') { lineEndChar = '\n'; continue; } lineEndChar = -1; lineStart = sourceCursor - 1; lineno++; } if (c <= 127) { if (c == '\n' || c == '\r') { lineEndChar = c; c = '\n'; } } else { if (isJSFormatChar(c)) { continue; } if (ScriptRuntime.isJSLineTerminator(c)) { lineEndChar = c; c = '\n'; } } if (charno == -1) { charno = getOffset(); } return c; } } private int getCharIgnoreLineEnd() { if (ungetCursor != 0) { cursor++; --ungetCursor; if (charno == -1) { charno = getOffset(); } return ungetBuffer[ungetCursor]; } for(;;) { int c; if (sourceCursor == sourceEnd) { hitEOF = true; if (charno == -1) { charno = getOffset(); } return EOF_CHAR; } cursor++; c = sourceString.

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS>charAt(sourceCursor++); if (c <= 127) { if (c == '\n' || c == '\r') { lineEndChar = c; c = '\n'; } } else { if (isJSFormatChar(c)) { continue; } if (ScriptRuntime.isJSLineTerminator(c)) { lineEndChar = c; c = '\n'; } } if (charno == -1) { charno = getOffset(); } return c; } } private void ungetCharIgnoreLineEnd(int c) { ungetBuffer[ungetCursor++] = c; cursor--; } /** * Returns the offset into the current line. */ final int getOffset() { return sourceCursor - lineStart - ungetCursor - 1; } // Set this to an initial non-null value so that the Parser has // something to retrieve even if an error has occurred and no // string is found. Fosters one class of error, but saves lots of // code. private String string = ""; private char[] stringBuffer = new char[128]; private int stringBufferTop; // Room to backtrace from to < on failed match of the last - in <!-- private final int[] ungetBuffer = new int[3]; private int ungetCursor; private boolean hitEOF = false; private int lineStart = 0; private int lineEndChar = -1; int lineno; private int charno = -1; private int initCharno; private int initLineno; private String sourceString; private int sourceEnd; // sourceCursor is an index into a small buffer that keeps a // sliding window of the source stream. int sourceCursor; // cursor is a monotonically increasing index into the original // source stream, tracking exactly how far scanning has progressed. // Its value is the index of the next character to be scanned. int cursor; // Record start and end positions of last scanned token. int tokenBeg; int tokenEnd; }

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS> registry.getNativeObjectType(JSTypeNative.UNKNOWN_TYPE)); this.name = name; } @Override public String getReferenceName() { return name; } @Override public String toString() { return name; } @Override public boolean isTemplateType() { return true; } }

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS>_THIS = DiagnosticType.warning( "JSC_USED_GLOBAL_THIS", "dangerous use of the global 'this' object"); private final AbstractCompiler compiler; /** * If {@code assignLhsChild != null}, then the node being traversed is * a descendant of the first child of an ASSIGN node. assignLhsChild's * parent is this ASSIGN node. */ private Node assignLhsChild = null; CheckGlobalThis(AbstractCompiler compiler) { this.compiler = compiler; } /** * Since this pass reports errors only when a global {@code this} keyword * is encountered, there is no reason to traverse non global contexts. */ public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.FUNCTION) { // Don't traverse functions that are constructors or have the @this // or @override annotation. JSDocInfo jsDoc = getFunctionJsDocInfo(n); if (jsDoc != null && (jsDoc.isConstructor() || jsDoc.isInterface() || jsDoc.hasThisType() || jsDoc.isOverride())) { return false; } // Don't traverse functions unless they would normally // be able to have a @this annotation associated with them. e.g., // var a = function() { }; // or // function a() {} // or // a.x = function() {}; // or // var a = {x: function() {}}; int pType = parent.getType(); if (!(pType == Token.BLOCK || pType == Token.SCRIPT || pType == Token.NAME || pType == Token.ASSIGN || // object literal keys pType == Token.STRING || pType == Token.NUMBER)) { return false; } // Don't traverse functions that are getting lent to a prototype. Node gramps = parent.getParent(); if (NodeUtil.isObjectLitKey(parent, gramps)) { JSDocInfo maybeLends = gramps.getJSDocInfo(); if (maybeLends != null && maybeLends.getLendsName() != null && maybeLends.getLendsName().endsWith(".prototype"))

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS> { return false; } } } if (parent != null && parent.getType() == Token.ASSIGN) { Node lhs = parent.getFirstChild(); Node rhs = lhs.getNext(); if (n == lhs) { // Always traverse the left side of the assignment. To handle // nested assignments properly (e.g., (a = this).property = c;), // assignLhsChild should not be overridden. if (assignLhsChild == null) { assignLhsChild = lhs; } } else { // Only traverse the right side if it's not an assignment to a prototype // property or subproperty. if (NodeUtil.isGet(lhs)) { if (lhs.getType() == Token.GETPROP && lhs.getLastChild().getString().equals("prototype")) { return false; } Node llhs = lhs.getFirstChild(); if (llhs.getType() == Token.GETPROP && llhs.getLastChild().getString().equals("prototype")) { return false; } } } } return true; } public void visit(NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.THIS && shouldReportThis(n, parent)) { compiler.report(t.makeError(n, GLOBAL_THIS)); } if (n == assignLhsChild) { assignLhsChild = null; } } private boolean shouldReportThis(Node n, Node parent) { if (assignLhsChild != null) { // Always report a THIS on the left side of an assign. return true; } // Also report a THIS with a property access. return parent != null && NodeUtil.isGet(parent); } /** * Gets a function's JSDoc information, if it has any. Checks for a few * patterns (ellipses show where JSDoc would be): * <pre> * ... function() {} * ... x = function() {}; * var ... x = function() {}; * ... var x = function() {}; * </pre> */ private JSDocInfo getFunctionJsDocInfo(Node n) { JSDocInfo jsDoc = n.getJSDocInfo();

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS>ATE_PARAM", "Parse error. {0}"); static final DiagnosticType BAD_JSDOC_ANNOTATION = DiagnosticType.warning("JSC_BAD_JSDOC_ANNOTATION", "Parse error. {0}"); // A map of Rhino messages to their DiagnosticType. private final Map<Pattern, DiagnosticType> typeMap; private final AbstractCompiler compiler; /** * For each message such as "Not a good use of {0}", replace the place * holder {0} with a wild card that matches all possible strings. * Also put the any non-place-holder in quotes for regex matching later. */ private Pattern replacePlaceHolders(String s) { s = Pattern.quote(s); return Pattern.compile(s.replaceAll("\\{\\d+\\}", "\\\\E.*\\\\Q")); } private RhinoErrorReporter(AbstractCompiler compiler) { this.compiler = compiler; typeMap = ImmutableMap.of( // Extra @fileoverview replacePlaceHolders( ScriptRuntime.getMessage0("msg.jsdoc.fileoverview.extra")), EXTRA_FILEOVERVIEW, // Trailing comma replacePlaceHolders( com.google.javascript.jscomp.mozilla.rhino.ScriptRuntime .getMessage0("msg.extra.trailing.comma")), TRAILING_COMMA, // Duplicate parameter replacePlaceHolders( com.google.javascript.jscomp.mozilla.rhino.ScriptRuntime .getMessage0("msg.dup.parms")), DUPLICATE_PARAM, // Unknown @annotations. replacePlaceHolders(ScriptRuntime.getMessage0("msg.bad.jsdoc.tag")), BAD_JSDOC_ANNOTATION, // Type annotation errors. Pattern.compile("^Bad type annotation.*"), TYPE_PARSE_ERROR); } public static com.google.javascript.jscomp.mozilla.rhino.ErrorReporter forNewRhino(AbstractCompiler compiler) { return new NewRhinoErrorReporter(compiler); } public static ErrorReporter forOldRhino(AbstractCompiler compiler) { return new OldRhinoErrorReporter(compiler); } public void warning(String message, String sourceName, int line, String lineSource, int lineOffset) { compiler.report(

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS>Format); } /** * Create a DiagnosticType at a given CheckLevel. * * @param name An identifier * @param level Either CheckLevel.ERROR or CheckLevel.WARNING * @param descriptionFormat A format string * @return A new DiagnosticType */ public static DiagnosticType make(String name, CheckLevel level, String descriptionFormat) { return new DiagnosticType(name, level, new MessageFormat(descriptionFormat)); } /** * Create a DiagnosticType. Private to force use of static factory methods. */ private DiagnosticType(String key, CheckLevel level, MessageFormat format) { this.key = key; this.defaultLevel = level; this.format = format; this.level = this.defaultLevel; } /** * Create a description from the MessageFormat and the arguments. * Used by unit tests. */ String format(Object ... arguments) { return format.format(arguments); } @Override public int compareTo(DiagnosticType diagnosticType) { return key.compareTo(diagnosticType.key); } @Override public String toString() { return key + ": " + format.toPattern(); } }

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS> public boolean isNullable() { return false; } @Override public TernaryValue testForEquality(JSType that) { TernaryValue result = super.testForEquality(that); if (result != null) { return result; } if (that.isUnknownType() || that.isSubtype( getNativeType(JSTypeNative.NUMBER_STRING_BOOLEAN)) || that.isObject()) { return UNKNOWN; } return FALSE; } @Override public boolean isBooleanValueType() { return true; } @Override public boolean matchesNumberContext() { return true; } @Override public boolean matchesStringContext() { return true; } @Override public boolean matchesObjectContext() { // TODO(user): Revisit this for ES4, which is stricter. return true; } @Override public JSType autoboxesTo() { return getNativeType(JSTypeNative.BOOLEAN_OBJECT_TYPE); } @Override public String toString() { return getDisplayName(); } @Override public String getDisplayName() { return "boolean"; } @Override public BooleanLiteralSet getPossibleToBooleanOutcomes() { return BooleanLiteralSet.BOTH; } @Override public <T> T visit(Visitor<T> visitor) { return visitor.caseBooleanType(); } }

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS>.newArrayList(); for (Name name : namespace.getNameIndex().values()) { if (name.docInfo != null && name.docInfo.isDefine()) { // Process defines should not depend on check types being enabled, // so we look for the JSDoc instead of the inferred type. if (isValidDefineType(name.docInfo.getType())) { allDefines.add(name); } else { JSError error = JSError.make( name.declaration.sourceName, name.declaration.node, INVALID_DEFINE_TYPE_ERROR); compiler.report(error); } } else if (name.refs != null) { for (Ref ref : name.refs) { Node n = ref.node; Node parent = ref.node.getParent(); JSDocInfo info = n.getJSDocInfo(); if (info == null && parent.getType() == Token.VAR && parent.hasOneChild()) { info = parent.getJSDocInfo(); } if (info != null && info.isDefine()) { allDefines.add(name); break; } } } } CollectDefines pass = new CollectDefines(compiler, allDefines); NodeTraversal.traverse(compiler, root, pass); return pass.getAllDefines(); } /** * Finds all assignments to @defines, and figures out the last value of * the @define. */ private static final class CollectDefines implements Callback { private final AbstractCompiler compiler; private final Map<String, DefineInfo> assignableDefines; private final Map<String, DefineInfo> allDefines; private final Map<Node, RefInfo> allRefInfo; // A hack that allows us to remove ASSIGN/VAR statements when // we're currently visiting one of the children of the assign. private Node lvalueToRemoveLater = null; // A stack tied to the node traversal, to keep track of whether // we're in a conditional block. If 1 is at the top, assignment to // a define is allowed. Otherwise, it's not allowed. private final Deque<Integer> assignAllowed; CollectDefines(AbstractCompiler compiler, List<Name> listOfDefines) { this.compiler = compiler; this.allDefines = Maps.newHashMap

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS> all the defines are at the top of the bundle. for (DefineInfo info : assignableDefines.values()) { setDefineInfoNotAssignable(info, t); } assignableDefines.clear(); } } updateAssignAllowedStack(n, false); } /** * Determines whether assignment to a define should be allowed * in the subtree of the given node, and if not, records that fact. * * @param n The node whose subtree we're about to enter or exit. * @param entering True if we're entering the subtree, false otherwise. */ private void updateAssignAllowedStack(Node n, boolean entering) { switch (n.getType()) { case Token.CASE: case Token.FOR: case Token.FUNCTION: case Token.HOOK: case Token.IF: case Token.SWITCH: case Token.WHILE: if (entering) { assignAllowed.push(0); } else { assignAllowed.remove(); } break; } } /** * Determines whether assignment to a define should be allowed * at the current point of the traversal. */ private boolean isAssignAllowed() { return assignAllowed.element() == 1; } /** * Tracks the given define. * * @param t The current traversal, for context. * @param name The full name for this define. * @param value The value assigned to the define. * @param valueParent The parent node of value. * @return Whether we should remove this assignment from the parse tree. */ private boolean processDefineAssignment(NodeTraversal t, String name, Node value, Node valueParent) { if (value == null || !NodeUtil.isValidDefineValue(value, allDefines.keySet())) { compiler.report( t.makeError(value, INVALID_DEFINE_INIT_ERROR, name)); } else if (!isAssignAllowed()) { compiler.report( t.makeError(valueParent, NON_GLOBAL_DEFINE_INIT_ERROR, name)); } else { DefineInfo info = allDefines.get(name); if (info == null) { // First declaration of this define. info = new DefineInfo(value, valueParent); allDefines.put(name, info

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS>); assignableDefines.put(name, info); } else if (info.recordAssignment(value)) { // The define was already initialized, but this is a safe // re-assignment. return true; } else { // The define was already initialized, and this is an unsafe // re-assignment. compiler.report( t.makeError(valueParent, DEFINE_NOT_ASSIGNABLE_ERROR, name, info.getReasonWhyNotAssignable())); } } return false; } /** * Gets the parent node of the value for any assignment to a Name. * For example, in the assignment * {@code var x = 3;} * the parent would be the NAME node. */ private static Node getValueParent(Ref ref) { // there are two types of declarations: VARs and ASSIGNs return ref.node.getParent() != null && ref.node.getParent().getType() == Token.VAR ? ref.node : ref.node.getParent(); } /** * Records the fact that because of the current node in the node traversal, * the define can't ever be assigned again. * * @param info Represents the define variable. * @param t The current traversal. */ private void setDefineInfoNotAssignable(DefineInfo info, NodeTraversal t) { info.setNotAssignable(format(REASON_DEFINE_NOT_ASSIGNABLE, t.getLineNumber(), t.getSourceName())); } /** * A simple data structure for associating a Ref with the name * that it references. */ private static class RefInfo { final Ref ref; final Name name; RefInfo(Ref ref, Name name) { this.ref = ref; this.name = name; } } } /** * A simple class for storing information about a define. * Gathers the initial value, the last assigned value, and whether * the define can be safely assigned a new value. */ private static final class DefineInfo { public final Node initialValueParent; public final Node initialValue; private Node lastValue; private boolean isAssignable; private String reasonNotAssignable; /** * Initializes a define. */ public DefineInfo(Node initialValue, Node initialValueParent) {

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS> this.initialValueParent = initialValueParent; this.initialValue = initialValue; lastValue = initialValue; isAssignable = true; } /** * Records the fact that this define can't be assigned a value anymore. * * @param reason A message describing the reason why it can't be assigned. */ public void setNotAssignable(String reason) { isAssignable = false; reasonNotAssignable = reason; } /** * Gets the reason why a define is not assignable. */ public String getReasonWhyNotAssignable() { return reasonNotAssignable; } /** * Records an assigned value. * * @return False if there was an error. */ public boolean recordAssignment(Node value) { lastValue = value; return isAssignable; } /** * Gets the last assigned value. */ public Node getLastValue() { return lastValue; } } }

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS>Callback) { this.scopeCallback = (ScopedCallback) cb; } this.compiler = compiler; this.sourceName = ""; this.scopeCreator = scopeCreator; } private void throwUnexpectedException(Exception unexpectedException) { // If there's an unexpected exception, try to get the // line number of the code that caused it. String message = unexpectedException.getMessage(); // TODO(user): It is possible to get more information if curNode or // its parent is missing. We still have the scope stack in which it is still // very useful to find out at least which function caused the exception. if (!sourceName.isEmpty()) { message = unexpectedException.getMessage() + "\n" + formatNodeContext("Node", curNode) + (curNode == null ? "" : formatNodeContext("Parent", curNode.getParent())); } compiler.throwInternalError(message, unexpectedException); } private String formatNodeContext(String label, Node n) { if (n == null) { return " " + label + ": NULL"; } return " " + label + "(" + n.toString(false, false, false) + "): " + formatNodePosition(n); } /** * Traverses a parse tree recursively. */ public void traverse(Node root) { try { sourceName = ""; curNode = root; pushScope(root); traverseBranch(root, null); popScope(); } catch (Exception unexpectedException) { throwUnexpectedException(unexpectedException); } } public void traverseRoots(Node ... roots) { traverseRoots(Lists.newArrayList(roots)); } public void traverseRoots(List<Node> roots) { if (roots.isEmpty()) { return; } try { Node scopeRoot = roots.get(0).getParent(); Preconditions.checkState(scopeRoot != null); sourceName = ""; curNode = scopeRoot; pushScope(scopeRoot); for (Node root : roots) { Preconditions.checkState(root.getParent() == scopeRoot); traverseBranch(root, scopeRoot); } popScope(); } catch (Exception unexpectedException) { throwUnexpectedException(unexpectedException); } }

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS> private static final String MISSING_SOURCE = "[source unknown]"; private String formatNodePosition(Node n) { if (n == null) { return MISSING_SOURCE + "\n"; } int lineNumber = n.getLineno(); int columnNumber = n.getCharno(); String src = compiler.getSourceLine(sourceName, lineNumber); if (src == null) { src = MISSING_SOURCE; } return sourceName + ":" + lineNumber + ":" + columnNumber + "\n" + src + "\n"; } /** * Traverses a parse tree recursively with a scope, starting with the given * root. This should only be used in the global scope. Otherwise, use * {@link #traverseAtScope}. */ void traverseWithScope(Node root, Scope s) { Preconditions.checkState(s.isGlobal()); sourceName = ""; curNode = root; pushScope(s); traverseBranch(root, null); popScope(); } /** * Traverses a parse tree recursively with a scope, starting at that scope's * root. */ void traverseAtScope(Scope s) { Node n = s.getRootNode(); if (n.getType() == Token.FUNCTION) { // We need to do some extra magic to make sure that the scope doesn't // get re-created when we dive into the function. sourceName = getSourceName(n); curNode = n; pushScope(s); Node args = n.getFirstChild().getNext(); Node body = args.getNext(); traverseBranch(args, n); traverseBranch(body, n); popScope(); } else { traverseWithScope(n, s); } } /** * Traverses an inner node recursively with a refined scope. An inner node may * be any node with a non {@code null} parent (i.e. all nodes except the * root). * * @param node the node to traverse * @param parent the node's parent, it may be not be {@code null} * @param refinedScope the refined scope of the scope currently at the top of * the scope stack or in trivial cases that very scope or {@code null} */

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS>.g. when entering a function). */ private void pushScope(Node node) { Preconditions.checkState(curNode != null); scopeRoots.push(node); cfgs.push(null); if (scopeCallback != null) { scopeCallback.enterScope(this); } } /** Creates a new scope (e.g. when entering a function). */ private void pushScope(Scope s) { Preconditions.checkState(curNode != null); scopes.push(s); cfgs.push(null); if (scopeCallback != null) { scopeCallback.enterScope(this); } } /** Pops back to the previous scope (e.g. when leaving a function). */ private void popScope() { if (scopeCallback != null) { scopeCallback.exitScope(this); } if (scopeRoots.isEmpty()) { scopes.pop(); } else { scopeRoots.pop(); } cfgs.pop(); } /** Gets the current scope. */ public Scope getScope() { Scope scope = scopes.isEmpty() ? null : scopes.peek(); if (scopeRoots.isEmpty()) { return scope; } Iterator<Node> it = scopeRoots.descendingIterator(); while (it.hasNext()) { scope = scopeCreator.createScope(it.next(), scope); scopes.push(scope); } scopeRoots.clear(); return scope; } /** Gets the control flow graph for the current JS scope. */ public ControlFlowGraph<Node> getControlFlowGraph() { if (cfgs.peek() == null) { ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); cfa.process(null, getScopeRoot()); cfgs.pop(); cfgs.push(cfa.getCfg()); } return cfgs.peek(); } /** Returns the current scope's root. */ public Node getScopeRoot() { if (scopeRoots.isEmpty()) { return scopes.peek().getRootNode(); } else { return scopeRoots.peek(); } } /** * Determines whether the traversal is currently in the global scope. */ boolean inGlobalScope() { return getScopeDepth() <= 1

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS> false; idGenerators = Collections.emptySet(); replaceStringsFunctionDescriptions = Collections.emptyList(); replaceStringsPlaceholderToken = ""; replaceStringsReservedStrings = Collections.emptySet(); // Output printInputDelimiter = false; prettyPrint = false; lineBreak = false; reportPath = null; tracer = TracerMode.OFF; colorizeErrorOutput = false; errorFormat = ErrorFormat.SINGLELINE; warningsGuard = null; debugFunctionSideEffectsPath = null; jsOutputFile = ""; externExports = false; nameReferenceReportPath = null; nameReferenceGraphPath = null; // Debugging aliasHandler = NULL_ALIAS_TRANSFORMATION_HANDLER; operaCompoundAssignFix = true; } /** * Returns the map of define replacements. */ public Map<String, Node> getDefineReplacements() { return getReplacementsHelper(defineReplacements); } /** * Returns the map of tweak replacements. */ public Map<String, Node> getTweakReplacements() { return getReplacementsHelper(tweakReplacements); } /** * Creates a map of String->Node from a map of String->Number/String/Boolean. */ private static Map<String, Node> getReplacementsHelper( Map<String, Object> source) { Map<String, Node> map = Maps.newHashMap(); for (Map.Entry<String, Object> entry : source.entrySet()) { String name = entry.getKey(); Object value = entry.getValue(); if (value instanceof Boolean) { map.put(name, ((Boolean) value).booleanValue() ? new Node(Token.TRUE) : new Node(Token.FALSE)); } else if (value instanceof Integer) { map.put(name, Node.newNumber(((Integer) value).intValue())); } else if (value instanceof Double) { map.put(name, Node.newNumber(((Double) value).doubleValue())); } else { Preconditions.checkState(value instanceof String); map.put(name, Node.newString((String) value)); } } return map; } /** * Sets the value of the {@code @define} variable in JS * to a boolean literal. */ public void

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS> setDefineToBooleanLiteral(String defineName, boolean value) { defineReplacements.put(defineName, new Boolean(value)); } /** * Sets the value of the {@code @define} variable in JS to a * String literal. */ public void setDefineToStringLiteral(String defineName, String value) { defineReplacements.put(defineName, value); } /** * Sets the value of the {@code @define} variable in JS to a * number literal. */ public void setDefineToNumberLiteral(String defineName, int value) { defineReplacements.put(defineName, new Integer(value)); } /** * Sets the value of the {@code @define} variable in JS to a * number literal. */ public void setDefineToDoubleLiteral(String defineName, double value) { defineReplacements.put(defineName, new Double(value)); } /** * Sets the value of the tweak in JS * to a boolean literal. */ public void setTweakToBooleanLiteral(String tweakId, boolean value) { tweakReplacements.put(tweakId, new Boolean(value)); } /** * Sets the value of the tweak in JS to a * String literal. */ public void setTweakToStringLiteral(String tweakId, String value) { tweakReplacements.put(tweakId, value); } /** * Sets the value of the tweak in JS to a * number literal. */ public void setTweakToNumberLiteral(String tweakId, int value) { tweakReplacements.put(tweakId, new Integer(value)); } /** * Sets the value of the tweak in JS to a * number literal. */ public void setTweakToDoubleLiteral(String tweakId, double value) { tweakReplacements.put(tweakId, new Double(value)); } /** * Skip all possible passes, to make the compiler as fast as possible. */ public void skipAllCompilerPasses() { skipAllPasses = true; } /** * Whether the warnings guard in this Options object enables the given * group of warnings. */ boolean enables(DiagnosticGroup type) { return warningsGuard != null && warningsGuard.enables

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS>Configuration( String placeholderToken, List<String> functionDescriptors) { this.replaceStringsPlaceholderToken = placeholderToken; this.replaceStringsFunctionDescriptions = Lists.newArrayList(functionDescriptors); } public void setRewriteNewDateGoogNow(boolean rewrite) { this.rewriteNewDateGoogNow = rewrite; } public void setRemoveAbstractMethods(boolean remove) { this.removeAbstractMethods = remove; } public void setRemoveClosureAsserts(boolean remove) { this.removeClosureAsserts = remove; } /** * If true, name anonymous functions only. All other passes will be skipped. */ public void setNameAnonymousFunctionsOnly(boolean value) { this.nameAnonymousFunctionsOnly = value; } public void lineLengthThreshold(int value) { this.lineLengthThreshold = value; } public void setColorizeErrorOutput(boolean colorizeErrorOutput) { this.colorizeErrorOutput = colorizeErrorOutput; } public boolean shouldColorizeErrorOutput() { return colorizeErrorOutput; } /** * If true, chain calls to functions that return this. */ public void setChainCalls(boolean value) { this.chainCalls = value; } /** * If true, accept `const' keyword. */ public void setAcceptConstKeyword(boolean value) { this.acceptConstKeyword = value; } /** * Enable runtime type checking, which adds JS type assertions for debugging. * * @param logFunction A JS function to be used for logging runtime type * assertion failures. */ public void enableRuntimeTypeCheck(String logFunction) { this.runtimeTypeCheck = true; this.runtimeTypeCheckLogFunction = logFunction; } public void disableRuntimeTypeCheck() { this.runtimeTypeCheck = false; } public void setGenerateExports(boolean generateExports) { this.generateExports = generateExports; } public void setCodingConvention(CodingConvention codingConvention) { this.codingConvention = codingConvention; } public CodingConvention getCodingConvention() { return codingConvention; } /** * Sort inputs by their goog.provide/goog.require calls, and prune inputs * whose symbols are not required. */ public void

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS> } @Override public void visit(NodeTraversal t, Node n, Node parent) { // Note: Constant properties annotations are not propagated. if (n.getType() == Token.NAME) { if (n.getString().isEmpty()) { return; } JSDocInfo info = null; // Find the JSDocInfo for a top level variable. Var var = t.getScope().getVar(n.getString()); if (var != null) { info = var.getJSDocInfo(); } boolean shouldBeConstant = (info != null && info.isConstant()) || NodeUtil.isConstantByConvention( compiler.getCodingConvention(), n, parent); boolean isMarkedConstant = n.getBooleanProp(Node.IS_CONSTANT_NAME); if (shouldBeConstant && !isMarkedConstant) { if (assertOnChange) { String name = n.getString(); throw new IllegalStateException( "Unexpected const change.\n" + " name: "+ name + "\n" + " parent:" + n.getParent().toStringTree()); } n.putBooleanProp(Node.IS_CONSTANT_NAME, true); } } } } /** * Walk the AST tree and verify that constant names are used consistently. */ static class VerifyConstants extends AbstractPostOrderCallback implements CompilerPass { final private AbstractCompiler compiler; final private boolean checkUserDeclarations; VerifyConstants(AbstractCompiler compiler, boolean checkUserDeclarations) { this.compiler = compiler; this.checkUserDeclarations = checkUserDeclarations; } @Override public void process(Node externs, Node root) { Node externsAndJs = root.getParent(); Preconditions.checkState(externsAndJs != null); Preconditions.checkState(externsAndJs.hasChild(externs)); NodeTraversal.traverseRoots( compiler, Lists.newArrayList(externs, root), this); } private Map<String, Boolean> constantMap = Maps.newHashMap(); @Override public void visit(NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.NAME) { String name = n.getString(); if (n.getString().isEmpty()) { return; } boolean isConst = n

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS>.getBooleanProp(Node.IS_CONSTANT_NAME); if (checkUserDeclarations) { boolean expectedConst = false; CodingConvention convention = compiler.getCodingConvention(); if (NodeUtil.isConstantName(n) || NodeUtil.isConstantByConvention(convention, n, parent)) { expectedConst = true; } else { expectedConst = false; JSDocInfo info = null; Var var = t.getScope().getVar(n.getString()); if (var != null) { info = var.getJSDocInfo(); } if (info != null && info.isConstant()) { expectedConst = true; } else { expectedConst = false; } } if (expectedConst) { Preconditions.checkState(expectedConst == isConst, "The name " + name + " is not annotated as constant."); } else { Preconditions.checkState(expectedConst == isConst, "The name " + name + " should not be annotated as constant."); } } Boolean value = constantMap.get(name); if (value == null) { constantMap.put(name, isConst); } else { Preconditions.checkState(value.booleanValue() == isConst, "The name " + name + " is not consistently annotated as " + "constant."); } } } } /** * Simplify the AST: * - VAR declarations split, so they represent exactly one child * declaration. * - WHILEs are converted to FORs * - FOR loop are initializers are moved out of the FOR structure * - LABEL node of children other than LABEL, BLOCK, WHILE, FOR, or DO are * moved into a block. * - Add constant annotations based on coding convention. */ static class NormalizeStatements implements Callback { private final AbstractCompiler compiler; private final boolean assertOnChange; NormalizeStatements(AbstractCompiler compiler, boolean assertOnChange) { this.compiler = compiler; this.assertOnChange = assertOnChange; } private void reportCodeChange(String changeDescription) { if (assertOnChange) { throw new IllegalStateException( "Normalize constraints violated:\n" + changeDescription); } compiler.reportCodeChange();

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS>HashSet(); /** * Remove duplicate VAR declarations encountered discovered during * scope creation. */ @Override public void onRedeclaration( Scope s, String name, Node n, CompilerInput input) { Preconditions.checkState(n.getType() == Token.NAME); Node parent = n.getParent(); Var v = s.getVar(name); if (v != null && s.isGlobal()) { // We allow variables to be duplicate declared if one // declaration appears in source and the other in externs. // This deals with issues where a browser built-in is declared // in one browser but not in another. if (v.isExtern() && !input.isExtern()) { if (hasOkDuplicateDeclaration.add(v)) { return; } } } // If name is "arguments", Var maybe null. if (v != null && v.getParentNode().getType() == Token.CATCH) { // Redeclaration of a catch expression variable is hard to model // without support for "with" expressions. // The EcmaScript spec (section 12.14), declares that a catch // "catch (e) {}" is handled like "with ({'e': e}) {}" so that // "var e" would refer to the scope variable, but any following // reference would still refer to "e" of the catch expression. // Until we have support for this disallow it. // Currently the Scope object adds the catch expression to the // function scope, which is technically not true but a good // approximation for most uses. // TODO(johnlenz): Consider improving how scope handles catch // expression. // Use the name of the var before it was made unique. name = MakeDeclaredNamesUnique.ContextualRenameInverter.getOrginalName( name); compiler.report( JSError.make( input.getName(), n, CATCH_BLOCK_VAR_ERROR, name)); } else if (v != null && parent.getType() == Token.FUNCTION) { if (v.getParentNode().getType() == Token.VAR) { s.undeclare(v); s.declare(name, n, n.getJSType(), v.input); replaceVarWithAssignment(v.getName

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS>Node(), v.getParentNode(), v.getParentNode().getParent()); } } else if (parent.getType() == Token.VAR) { Preconditions.checkState(parent.hasOneChild()); replaceVarWithAssignment(n, parent, parent.getParent()); } } /** * Remove the parent VAR. There are three cases that need to be handled: * 1) "var a = b;" which is replaced with "a = b" * 2) "label:var a;" which is replaced with "label:;". Ideally, the * label itself would be removed but that is not possible in the * context in which "onRedeclaration" is called. * 3) "for (var a in b) ..." which is replaced with "for (a in b)..." * Cases we don't need to handle are VARs with multiple children, * which have already been split into separate declarations, so there * is no need to handle that here, and "for (var a;;);", which has * been moved out of the loop. * The result of this is that in each case the parent node is replaced * which is generally dangerous in a traversal but is fine here with * the scope creator, as the next node of interest is the parent's * next sibling. */ private void replaceVarWithAssignment(Node n, Node parent, Node gramps) { if (n.hasChildren()) { // The * is being initialize, preserve the new value. parent.removeChild(n); // Convert "var name = value" to "name = value" Node value = n.getFirstChild(); n.removeChild(value); Node replacement = new Node(Token.ASSIGN, n, value); replacement.copyInformationFrom(parent); gramps.replaceChild(parent, NodeUtil.newExpr(replacement)); } else { // It is an empty reference remove it. if (NodeUtil.isStatementBlock(gramps)) { gramps.removeChild(parent); } else if (gramps.getType() == Token.FOR) { // This is the "for (var a in b)..." case. We don't need to worry // about initializers in "for

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS> public TernaryValue testForEquality(JSType that) { TernaryValue result = super.testForEquality(that); if (result != null) { return result; } if (that.isUnknownType() || that.isSubtype( getNativeType(JSTypeNative.OBJECT_NUMBER_STRING_BOOLEAN))) { return UNKNOWN; } return FALSE; } @Override public boolean isStringValueType() { return true; } @Override public boolean matchesNumberContext() { return true; } @Override public boolean matchesStringContext() { return true; } @Override public boolean matchesObjectContext() { // TODO(user): Revisit this for ES4, which is stricter. return true; } @Override public String toString() { return getDisplayName(); } @Override public String getDisplayName() { return "string"; } @Override public JSType autoboxesTo() { return getNativeType(JSTypeNative.STRING_OBJECT_TYPE); } @Override public BooleanLiteralSet getPossibleToBooleanOutcomes() { return BooleanLiteralSet.BOTH; } @Override public <T> T visit(Visitor<T> visitor) { return visitor.caseStringType(); } }

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS> reservedNames; this.prefix = prefix; // build the character arrays to use this.firstChars = reserveCharacters(FIRST_CHAR, reservedCharacters); this.nonFirstChars = reserveCharacters(NONFIRST_CHAR, reservedCharacters); checkPrefix(prefix); } /** * Provides the array of available characters based on the specified arrays. * @param chars The list of characters that are legal * @param reservedCharacters The characters that should not be used * @return An array of characters to use. Will return the chars array if * reservedCharacters is null or empty, otherwise creates a new array. */ static char[] reserveCharacters(char[] chars, char[] reservedCharacters) { if (reservedCharacters == null || reservedCharacters.length == 0) { return chars; } Set<Character> charSet = Sets.newLinkedHashSet(Chars.asList(chars)); for (char reservedCharacter : reservedCharacters) { charSet.remove(reservedCharacter); } return Chars.toArray(charSet); } /** Validates a name prefix. */ private void checkPrefix(String prefix) { if (prefix.length() > 0) { // Make sure that prefix starts with a legal character. if (!contains(firstChars, prefix.charAt(0))) { throw new IllegalArgumentException("prefix must start with one of: " + Arrays.toString(firstChars)); } for (int pos = 1; pos < prefix.length(); ++pos) { if (!contains(nonFirstChars, prefix.charAt(pos))) { throw new IllegalArgumentException("prefix has invalid characters, " + "must be one of: " + Arrays.toString(nonFirstChars)); } } } } private boolean contains(char[] arr, char c) { for (int i = 0; i < arr.length; i++) { if (arr[i] == c) { return true; } } return false; } /** * Generates the next short name. */ String generateNextName() { while (true) { String name = prefix; int i = nameCount; if (name.isEmpty()) { int pos = i % firstChars.length; name += firstChars[pos]; i /=

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS> firstChars.length; } while (i > 0) { i--; int pos = i % nonFirstChars.length; name += nonFirstChars[pos]; i /= nonFirstChars.length; } nameCount++; // Make sure it's not a JS keyword or reserved name. if (TokenStream.isKeyword(name) || reservedNames.contains(name)) { continue; } return name; } } }

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS> * * @param js Inputs * @param expected Expected JS output */ public void test(String[] js, String[] expected) { test(js, expected, null); } /** * Verifies that the compiler pass's JS output matches the expected output, * or that an expected error is encountered. * * @param js Inputs * @param expected Expected JS output * @param error Expected error, or null if no error is expected */ public void test(String[] js, String[] expected, DiagnosticType error) { test(js, expected, error, null); } /** * Verifies that the compiler pass's JS output matches the expected output * and (optionally) that an expected warning is issued. Or, if an error is * expected, this method just verifies that the error is encountered. * * @param js Inputs * @param expected Expected JS output * @param error Expected error, or null if no error is expected * @param warning Expected warning, or null if no warning is expected */ public void test(String[] js, String[] expected, DiagnosticType error, DiagnosticType warning) { test(js, expected, error, warning, null); } /** * Verifies that the compiler pass's JS output matches the expected output * and (optionally) that an expected warning is issued. Or, if an error is * expected, this method just verifies that the error is encountered. * * @param js Inputs * @param expected Expected JS output * @param error Expected error, or null if no error is expected * @param warning Expected warning, or null if no warning is expected * @param description The description of the expected warning, * or null if no warning is expected or if the warning's description * should not be examined */ public void test(String[] js, String[] expected, DiagnosticType error, DiagnosticType warning, String description) { Compiler compiler = createCompiler(); lastCompiler = compiler; JSSourceFile[] inputs = new JSSourceFile[js.length]; for (int i = 0; i < js.length; i++) { inputs[i] = JSSourceFile.fromCode("input" + i, js[i]); } compiler.init(extern

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS>ourceFile[]{ JSSourceFile.fromCode("externs", externs) }; test(externsInputs, js, js, null, warning, description); } /** * Verifies that the compiler pass's JS output is the same as its input. * * @param js Inputs and outputs */ public void testSame(String[] js) { test(js, js); } /** * Verifies that the compiler pass's JS output is the same as its input, * and emits the given error. * * @param js Inputs and outputs * @param error Expected error, or null if no error is expected */ public void testSame(String[] js, DiagnosticType error) { test(js, js, error); } /** * Verifies that the compiler pass's JS output is the same as its input, * and emits the given error and warning. * * @param js Inputs and outputs * @param error Expected error, or null if no error is expected * @param warning Expected warning, or null if no warning is expected */ public void testSame( String[] js, DiagnosticType error, DiagnosticType warning) { test(js, js, error, warning); } /** * Verifies that the compiler pass's JS output is the same as the input. * * @param modules Module inputs */ public void testSame(JSModule[] modules) { testSame(modules, null); } /** * Verifies that the compiler pass's JS output is the same as the input. * * @param modules Module inputs * @param warning A warning, or null for no expected warning. */ public void testSame(JSModule[] modules, DiagnosticType warning) { try { String[] expected = new String[modules.length]; for (int i = 0; i < modules.length; i++) { expected[i] = ""; for (CompilerInput input : modules[i].getInputs()) { expected[i] += input.getSourceFile().getCode(); } } test(modules, expected, null, warning); } catch (IOException e) { throw new RuntimeException(e); } } /** * Verifies that the compiler pass's JS output matches the expected output * and (

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS> processing", recentChange.hasCodeChanged()); for (int i = 0; i < numRepetitions; ++i) { if (compiler.getErrorCount() == 0) { errorManagers[i] = new BlackHoleErrorManager(compiler); // Only run the type checking pass once, if asked. // Running it twice can cause unpredictable behavior because duplicate // objects for the same type are created, and the type system // uses reference equality to compare many types. if (typeCheckEnabled && i == 0) { TypeCheck check = createTypeCheck(compiler, typeCheckLevel); check.processForTesting(externsRoot, mainRoot); } // Only run the normalize pass once, if asked. if (normalizeEnabled && i == 0) { normalizeActualCode(compiler, externsRoot, mainRoot); } if (markNoSideEffects && i == 0) { MarkNoSideEffectCalls mark = new MarkNoSideEffectCalls(compiler); mark.process(externsRoot, mainRoot); } recentChange.reset(); getProcessor(compiler).process(externsRoot, mainRoot); if (checkLineNumbers) { (new LineNumberCheck(compiler)).process(externsRoot, mainRoot); } hasCodeChanged = hasCodeChanged || recentChange.hasCodeChanged(); aggregateWarningCount += errorManagers[i].getWarningCount(); aggregateWarnings.addAll(Lists.newArrayList(compiler.getWarnings())); if (normalizeEnabled) { boolean verifyDeclaredConstants = true; new Normalize.VerifyConstants(compiler, verifyDeclaredConstants) .process(externsRoot, mainRoot); } } } if (error == null) { assertEquals( "Unexpected error(s): " + Joiner.on("\n").join(compiler.getErrors()), 0, compiler.getErrorCount()); // Verify the symbol table. ErrorManager symbolTableErrorManager = new BlackHoleErrorManager(compiler); Node expectedRoot = parseExpectedJs(expected); expectedRoot.detachFromParent(); JSError[] stErrors = symbolTableErrorManager.getErrors(); if (expectedSymbolTableError != null) { assertEquals("There should be one error.", 1, stErrors.length); assertEquals(expectedSymbolTable

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS>Error, stErrors[0].getType()); } else { assertEquals("Unexpected symbol table error(s): " + Joiner.on("\n").join(stErrors), 0, stErrors.length); } if (warning == null) { assertEquals( "Unexpected warning(s): " + Joiner.on("\n").join(aggregateWarnings), 0, aggregateWarningCount); } else { assertEquals("There should be one warning, repeated " + numRepetitions + " time(s).", numRepetitions, aggregateWarningCount); for (int i = 0; i < numRepetitions; ++i) { JSError[] warnings = errorManagers[i].getWarnings(); JSError actual = warnings[0]; assertEquals(warning, actual.getType()); // Make sure that source information is always provided. if (!allowSourcelessWarnings) { assertTrue("Missing source file name in warning", actual.sourceName != null && !actual.sourceName.isEmpty()); assertTrue("Missing line number in warning", -1 != actual.lineNumber); assertTrue("Missing char number in warning", -1 != actual.getCharno()); } if (description != null) { assertEquals(description, actual.description); } } } if (normalizeEnabled) { normalizeActualCode(compiler, externsRootClone, mainRootClone); } boolean codeChange = !mainRootClone.isEquivalentTo(mainRoot); boolean externsChange = !externsRootClone.isEquivalentTo(externsRoot); // Generally, externs should not be change by the compiler passes. if (externsChange && !allowExternsChanges) { String explanation = externsRootClone.checkTreeEquals(externsRoot); fail("Unexpected changes to externs" + "\nExpected: " + compiler.toSource(externsRootClone) + "\nResult: " + compiler.toSource(externsRoot) + "\n" + explanation); } if (!codeChange && !externsChange) { assertFalse( "compiler.reportCodeChange() was called " + "even though nothing changed", hasCodeChanged); } else { assertTrue("compiler.reportCodeChange() should have been called", hasCodeChanged

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS>Error : compiler.getWarnings()) { warnings += actualError.description + "\n"; } assertEquals("There should be one warning. " + warnings, 1, compiler.getWarningCount()); assertEquals(warnings, warning, compiler.getWarnings()[0].getType()); } } } private void normalizeActualCode( Compiler compiler, Node externsRoot, Node mainRoot) { Normalize normalize = new Normalize(compiler, false); normalize.process(externsRoot, mainRoot); } /** * Parses expected js inputs and returns the root of the parse tree. */ protected Node parseExpectedJs(String[] expected) { Compiler compiler = createCompiler(); JSSourceFile[] inputs = new JSSourceFile[expected.length]; for (int i = 0; i < expected.length; i++) { inputs[i] = JSSourceFile.fromCode("expected" + i, expected[i]); } compiler.init(externsInputs, inputs, getOptions()); Node root = compiler.parseInputs(); assertTrue("Unexpected parse error(s): " + Joiner.on("\n").join(compiler.getErrors()), root != null); Node externsRoot = root.getFirstChild(); Node mainRoot = externsRoot.getNext(); // Only run the normalize pass, if asked. if (normalizeEnabled && normalizeExpected && !compiler.hasErrors()) { Normalize normalize = new Normalize(compiler, false); normalize.process(externsRoot, mainRoot); } return mainRoot; } protected Node parseExpectedJs(String expected) { return parseExpectedJs(new String[] {expected}); } /** * Generates a list of modules from a list of inputs, such that each module * depends on the module before it. */ static JSModule[] createModuleChain(String... inputs) { JSModule[] modules = createModules(inputs); for (int i = 1; i < modules.length; i++) { modules[i].addDependency(modules[i - 1]); } return modules; } /** * Generates a list of modules from a list of inputs, such that each module * depends on the first module. */ static JSModule[] createModuleStar(String... inputs) {

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS> JSModule[] modules = createModules(inputs); for (int i = 1; i < modules.length; i++) { modules[i].addDependency(modules[0]); } return modules; } /** * Generates a list of modules from a list of inputs, such that modules * form a bush formation. In a bush formation, module 2 depends * on module 1, and all other modules depend on module 2. */ static JSModule[] createModuleBush(String ... inputs) { Preconditions.checkState(inputs.length > 2); JSModule[] modules = createModules(inputs); for (int i = 1; i < modules.length; i++) { modules[i].addDependency(modules[i == 1 ? 0 : 1]); } return modules; } /** * Generates a list of modules from a list of inputs, such that modules * form a tree formation. In a tree formation, module N depends on * module `floor(N/2)`, So the modules form a balanced binary tree. */ static JSModule[] createModuleTree(String ... inputs) { JSModule[] modules = createModules(inputs); for (int i = 1; i < modules.length; i++) { modules[i].addDependency(modules[(i - 1) / 2]); } return modules; } /** * Generates a list of modules from a list of inputs. Does not generate any * dependencies between the modules. */ static JSModule[] createModules(String... inputs) { JSModule[] modules = new JSModule[inputs.length]; for (int i = 0; i < inputs.length; i++) { JSModule module = modules[i] = new JSModule("m" + i); module.add(JSSourceFile.fromCode("i" + i, inputs[i])); } return modules; } private static class BlackHoleErrorManager extends BasicErrorManager { private BlackHoleErrorManager(Compiler compiler) { compiler.setErrorManager(this); } @Override public void println(CheckLevel level, JSError error) {} @Override public void printSummary() {} } Compiler createCompiler() { Compiler compiler

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS>+=1"); testParseError("(x||y)+=1", INVALID_ASSIGNMENT_TARGET); testParseError("(x?y:z)+=1", INVALID_ASSIGNMENT_TARGET); testParseError("f()+=1", INVALID_ASSIGNMENT_TARGET); testParseError("f()++", INVALID_INCREMENT_TARGET); testParseError("f()--", INVALID_DECREMENT_TARGET); testParseError("++f()", INVALID_INCREMENT_TARGET); testParseError("--f()", INVALID_DECREMENT_TARGET); } private void testNoParseError(String string) { testParseError(string, (String)null); } private void testParseError(String string, String error) { testParseError(string, error == null ? null : new String[] { error }); } private void testParseError(String string, String[] errors) { Node root = newParse(string, new TestErrorReporter(errors, null)); assertTrue("unexpected warnings reported", errorReporter.hasEncounteredAllWarnings()); assertTrue("expected error were not reported", errorReporter.hasEncounteredAllErrors()); } private void assertMarkerPosition(Node n, int lineno, int charno) { int count = 0; for (JSDocInfo.Marker marker : n.getJSDocInfo().getMarkers()) { assertEquals(lineno, marker.annotation.getStartLine()); assertEquals(charno, marker.annotation.getPositionOnStartLine()); count++; } assertEquals(1, count); } private void assertNodePosition(int lineno, int charno, Node n) { assertEquals("Line number", lineno, n.getLineno()); assertEquals("Column position", charno, n.getCharno()); } private void testNewParser(String code, String expected) { String actual = newParse(code).toStringTree(); assertEquals(expected, actual); } private void parse(String string) { String compare = newParse(string).checkTreeEquals(oldParse(string)); assertTrue(compare, compare == null); } private Node newParse(String string) { return newParse(string, new TestErrorReporter(null, null)); } private Node newParse(String string, TestErrorReporter errorReporter) { CompilerEnvirons environment = new CompilerEnvirons();

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS> the explanation of checked unknown types in JSTypeNative. private final boolean isChecked; UnknownType(JSTypeRegistry registry, boolean isChecked) { super(registry); this.isChecked = isChecked; } @Override public boolean isUnknownType() { return true; } @Override public boolean isCheckedUnknownType() { return isChecked; } @Override public boolean canAssignTo(JSType that) { return true; } @Override public boolean canBeCalled() { return true; } @Override public boolean matchesNumberContext() { return true; } @Override public boolean matchesObjectContext() { return true; } @Override public boolean matchesStringContext() { return true; } @Override public TernaryValue testForEquality(JSType that) { return UNKNOWN; } @Override public boolean isNullable() { return true; } @Override public boolean isSubtype(JSType that) { return true; } @Override public <T> T visit(Visitor<T> visitor) { return visitor.caseUnknownType(); } @Override public String toString() { return getReferenceName(); } @Override boolean defineProperty(String propertyName, JSType type, boolean inferred, boolean inExterns, Node propertyNode) { // nothing to define return true; } @Override public ObjectType getImplicitPrototype() { return null; } @Override public int getPropertiesCount() { return Integer.MAX_VALUE; } @Override void collectPropertyNames(Set<String> props) { } @Override public JSType getPropertyType(String propertyName) { return this; } @Override public boolean hasProperty(String propertyName) { return true; } @Override public FunctionType getConstructor() { return null; } @Override public String getReferenceName() { return isChecked ? "??" : "?"; } @Override public String getDisplayName() { return "Unknown"; } @Override public boolean hasDisplayName() { return true; } @Override public boolean isPropertyTypeDeclared(String propertyName) { return false; } @Override

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS> private static final long serialVersionUID = 1L; VoidType(JSTypeRegistry registry) { super(registry); } @Override public JSType restrictByNotNullOrUndefined() { return registry.getNativeType(JSTypeNative.NO_TYPE); } @Override public TernaryValue testForEquality(JSType that) { if (UNKNOWN.equals(super.testForEquality(that))) { return UNKNOWN; } if (that.isSubtype(this) || that.isSubtype(getNativeType(JSTypeNative.NULL_TYPE))) { return TRUE; } return FALSE; } @Override public boolean matchesNumberContext() { return false; } @Override public boolean matchesObjectContext() { return false; } @Override public boolean matchesStringContext() { return true; } @Override public boolean isVoidType() { return true; } @Override public String toString() { return getDisplayName(); } @Override public String getDisplayName() { return "undefined"; } @Override public BooleanLiteralSet getPossibleToBooleanOutcomes() { return BooleanLiteralSet.FALSE; } @Override public <T> T visit(Visitor<T> visitor) { return visitor.caseVoidType(); } }

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS>primarily for debugging). */ @Override public String toString() { return name; } /** * Removes any references to nodes of the AST. This method is needed to * allow the ASTs to be garbage collected if the modules are kept around. */ public void clearAsts() { for (CompilerInput input : inputs) { input.clearAst(); } } /** * Puts the JS files into a topologically sorted order by their dependencies. */ public void sortInputsByDeps(Compiler compiler) { // Set the compiler, so that we can parse requires/provides and report // errors properly. for (CompilerInput input : inputs) { input.setCompiler(compiler); } // Sort the JSModule in this order. try { List<CompilerInput> sortedList = (new SortedDependencies<CompilerInput>( Collections.<CompilerInput>unmodifiableList(inputs))) .getSortedList(); inputs.clear(); inputs.addAll(sortedList); } catch (CircularDependencyException e) { compiler.report( JSError.make(CIRCULAR_DEPENDENCY_ERROR, e.getMessage())); } } /** * Returns the given collection of modules in topological order. * * Note that this will return the modules in the same order if they are * already sorted, and in general, will only change the order as necessary to * satisfy the ordering dependencies. This can be important for cases where * the modules do not properly specify all dependencies. */ public static JSModule[] sortJsModules(Collection<JSModule> modules) throws CircularDependencyException { // Sort the JSModule in this order. List<JSModule> sortedList = (new SortedDependencies<JSModule>( Lists.newArrayList(modules))).getSortedList(); return sortedList.toArray(new JSModule[sortedList.size()]); } /** * @param dep the depth to set */ public void setDepth(int dep) { this.depth = dep; } /** * @return the depth */ public int getDepth() { return depth; } }

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS>; } else if (scope != ref.scope) { return true; } } return false; } /** * @param index The index into the references array to look for an * assigning declaration. * * This is either the declaration if a value is assigned (such as * "var a = 2", "function a()...", "... catch (a)..."). */ private boolean isInitializingDeclarationAt(int index) { Reference maybeInit = references.get(index); if (maybeInit.isInitializingDeclaration()) { // This is a declaration that represents the initial value. // Specifically, var declarations without assignments such as "var a;" // are not. return true; } return false; } /** * @param index The index into the references array to look for an * initialized assignment reference. That is, an assignment immediately * follow a variable declaration that itself does not initialize the * variable. */ private boolean isInitializingAssignmentAt(int index) { if (index < references.size() && index > 0) { Reference maybeDecl = references.get(index-1); if (maybeDecl.isVarDeclaration()) { Preconditions.checkState(!maybeDecl.isInitializingDeclaration()); Reference maybeInit = references.get(index); if (maybeInit.isSimpleAssignmentToName()) { return true; } } } return false; } /** * @return The reference that provides the value for the variable at the * time of the first read, if known, otherwise null. * * This is either the variable declaration ("var a = ...") or first * reference following the declaration if it is an assignment. */ Reference getInitializingReference() { if (isInitializingDeclarationAt(0)) { return references.get(0); } else if (isInitializingAssignmentAt(1)) { return references.get(1); } return null; } /** * Constants are allowed to be defined after their first use. */ Reference getInitializingReferenceForConstants() { int size = references.size(); for (int i = 0; i < size; i++) { if (isInitializingDeclarationAt(i) || isInitializingAssignmentAt(i)) { return references.

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS>; } } /** * A class common to all visitors that need to restrict the type based on * some {@code typeof}-like condition being false. All base cases return * their type. It is up to the subclasses to override the appropriate ones. */ abstract class RestrictByFalseTypeOfResultVisitor extends RestrictByTypeOfResultVisitor { @Override protected JSType caseTopType(JSType topType) { return topType; } public JSType caseNoObjectType() { return getNativeType(NO_OBJECT_TYPE); } public JSType caseBooleanType() { return getNativeType(BOOLEAN_TYPE); } public JSType caseFunctionType(FunctionType type) { return type; } public JSType caseNullType() { return getNativeType(NULL_TYPE); } public JSType caseNumberType() { return getNativeType(NUMBER_TYPE); } public JSType caseObjectType(ObjectType type) { return type; } public JSType caseStringType() { return getNativeType(STRING_TYPE); } public JSType caseVoidType() { return getNativeType(VOID_TYPE); } } /** * @see ChainableReverseAbstractInterpreter#getRestrictedByTypeOfResult */ private class RestrictByOneTypeOfResultVisitor extends RestrictByTypeOfResultVisitor { /** * A value known to be equal or not equal to the result of the * {@code typeOf} operation. */ private final String value; /** * {@code true} if the {@code typeOf} result is known to equal * {@code value}; {@code false} if it is known <em>not</em> to equal * {@code value}. */ private final boolean resultEqualsValue; RestrictByOneTypeOfResultVisitor(String value, boolean resultEqualsValue) { this.value = value; this.resultEqualsValue = resultEqualsValue; } /** * Computes whether the given result of a {@code typeof} operator matches * expectations, i.e. whether a type that gives such a result should be * kept. */ private boolean matchesExpectation(String result) { return result.equals(value) == resultEqualsValue; } @Override protected JSType caseTopType

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS>(JSType topType) { JSType result = topType; if (resultEqualsValue) { JSType typeByName = getNativeTypeForTypeOf(value); if (typeByName != null) { result = typeByName; } } return result; } public JSType caseNoObjectType() { return (value.equals("object") || value.equals("function")) == resultEqualsValue ? getNativeType(NO_OBJECT_TYPE) : null; } public JSType caseBooleanType() { return matchesExpectation("boolean") ? getNativeType(BOOLEAN_TYPE) : null; } public JSType caseFunctionType(FunctionType type) { return matchesExpectation("function") ? type : null; } public JSType caseNullType() { return matchesExpectation("object") ? getNativeType(NULL_TYPE) : null; } public JSType caseNumberType() { return matchesExpectation("number") ? getNativeType(NUMBER_TYPE) : null; } public JSType caseObjectType(ObjectType type) { if (value.equals("function")) { JSType ctorType = getNativeType(U2U_CONSTRUCTOR_TYPE); return resultEqualsValue && ctorType.isSubtype(type) ? ctorType : null; } return matchesExpectation("object") ? type : null; } public JSType caseStringType() { return matchesExpectation("string") ? getNativeType(STRING_TYPE) : null; } public JSType caseVoidType() { return matchesExpectation("undefined") ? getNativeType(VOID_TYPE) : null; } } /** * Returns a version of type where undefined is not present. */ final JSType getRestrictedWithoutUndefined(JSType type) { return type == null ? null : type.visit(restrictUndefinedVisitor); } /** * Returns a version of type where null is not present. */ final JSType getRestrictedWithoutNull(JSType type) { return type == null ? null : type.visit(restrictNullVisitor); } /** * Returns a version of {@code type} that is restricted by some knowledge * about the result of the {@code typeof} operation. * <p> * The behavior of the {@code typeof} operator can be summarized

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS> by the * following table: * <table> * <tr><th>type</th><th>result</th></tr> * <tr><td>{@code undefined}</td><td>"undefined"</td></tr> * <tr><td>{@code null}</td><td>"object"</td></tr> * <tr><td>{@code boolean}</td><td>"boolean"</td></tr> * <tr><td>{@code number}</td><td>"number"</td></tr> * <tr><td>{@code string}</td><td>"string"</td></tr> * <tr><td>{@code Object} (which doesn't implement [[Call]])</td> * <td>"object"</td></tr> * <tr><td>{@code Object} (which implements [[Call]])</td> * <td>"function"</td></tr> * </table> * @param type the type to restrict * @param value A value known to be equal or not equal to the result of the * {@code typeof} operation * @param resultEqualsValue {@code true} if the {@code typeOf} result is known * to equal {@code value}; {@code false} if it is known <em>not</em> to * equal {@code value} * @return the restricted type or null if no version of the type matches the * restriction */ JSType getRestrictedByTypeOfResult(JSType type, String value, boolean resultEqualsValue) { if (type == null) { if (resultEqualsValue) { JSType result = getNativeTypeForTypeOf(value); return result == null ? getNativeType(UNKNOWN_TYPE) : result; } else { return null; } } return type.visit( new RestrictByOneTypeOfResultVisitor(value, resultEqualsValue)); } JSType getNativeType(JSTypeNative typeId) { return typeRegistry.getNativeType(typeId); } /** * If we definitely know what a type is based on the typeof result, * return it. Otherwise, return null. * * The typeof operation in JS is poorly defined, and this function works * for both the native typeof and goog.typeOf. It should not be

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS> made public, * because its semantics are informally defined, and would be wrong in * the general case. */ private JSType getNativeTypeForTypeOf(String value) { if (value.equals("number")) { return getNativeType(NUMBER_TYPE); } else if (value.equals("boolean")) { return getNativeType(BOOLEAN_TYPE); } else if (value.equals("string")) { return getNativeType(STRING_TYPE); } else if (value.equals("undefined")) { return getNativeType(VOID_TYPE); } else if (value.equals("function")) { return getNativeType(U2U_CONSTRUCTOR_TYPE); } else { return null; } } }

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS> jsRoot) { Preconditions.checkState(scopeCreator == null); Preconditions.checkState(topScope == null); Preconditions.checkState(jsRoot.getParent() != null); Node externsAndJsRoot = jsRoot.getParent(); scopeCreator = new MemoizedScopeCreator(new TypedScopeCreator(compiler)); topScope = scopeCreator.createScope(externsAndJsRoot, null); TypeInferencePass inference = new TypeInferencePass(compiler, reverseInterpreter, topScope, scopeCreator); inference.process(externsRoot, jsRoot); process(externsRoot, jsRoot); return topScope; } public void check(Node node, boolean externs) { Preconditions.checkNotNull(node); NodeTraversal t = new NodeTraversal(compiler, this, scopeCreator); inExterns = externs; t.traverseWithScope(node, topScope); if (externs) { inferJSDocInfo.process(node, null); } else { inferJSDocInfo.process(null, node); } } private void checkNoTypeCheckSection(Node n, boolean enterSection) { switch (n.getType()) { case Token.SCRIPT: case Token.BLOCK: case Token.VAR: case Token.FUNCTION: case Token.ASSIGN: JSDocInfo info = n.getJSDocInfo(); if (info != null && info.isNoTypeCheck()) { if (enterSection) { noTypeCheckSection++; } else { noTypeCheckSection--; } } validator.setShouldReport(noTypeCheckSection == 0); break; } } private void report(NodeTraversal t, Node n, DiagnosticType diagnosticType, String... arguments) { if (noTypeCheckSection == 0) { t.report(n, diagnosticType, arguments); } } public boolean shouldTraverse( NodeTraversal t, Node n, Node parent) { checkNoTypeCheckSection(n, true); switch (n.getType()) { case Token.FUNCTION: // normal type checking final TypeCheck outerThis = this; final Scope outerScope = t.getScope(); final FunctionType functionType = (FunctionType) n.getJSType();

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS> final String functionPrivateName = n.getFirstChild().getString(); if (functionPrivateName != null && functionPrivateName.length() > 0 && outerScope.isDeclared(functionPrivateName, false) && // Ideally, we would want to check whether the type in the scope // differs from the type being defined, but then the extern // redeclarations of built-in types generates spurious warnings. !(outerScope.getVar( functionPrivateName).getType() instanceof FunctionType)) { report(t, n, FUNCTION_MASKS_VARIABLE, functionPrivateName); } // TODO(user): Only traverse the function's body. The function's // name and arguments are traversed by the scope creator, and ideally // should not be traversed by the type checker. break; } return true; } /** * This is the meat of the type checking. It is basically one big switch, * with each case representing one type of parse tree node. The individual * cases are usually pretty straightforward. * * @param t The node traversal object that supplies context, such as the * scope chain to use in name lookups as well as error reporting. * @param n The node being visited. * @param parent The parent of the node n. */ public void visit(NodeTraversal t, Node n, Node parent) { JSType childType; JSType leftType, rightType; Node left, right; // To be explicitly set to false if the node is not typeable. boolean typeable = true; switch (n.getType()) { case Token.NAME: typeable = visitName(t, n, parent); break; case Token.LP: // If this is under a FUNCTION node, it is a parameter list and can be // ignored here. if (parent.getType() != Token.FUNCTION) { ensureTyped(t, n, getJSType(n.getFirstChild())); } else { typeable = false; } break; case Token.COMMA: ensureTyped(t, n, getJSType(n.getLastChild())); break; case Token.TRUE: case Token.FALSE: ensureTyped(t, n, BOOLEAN_TYPE); break; case

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS>), "increment/decrement"); ensureTyped(t, n, NUMBER_TYPE); break; case Token.NOT: ensureTyped(t, n, BOOLEAN_TYPE); break; case Token.VOID: ensureTyped(t, n, VOID_TYPE); break; case Token.TYPEOF: ensureTyped(t, n, STRING_TYPE); break; case Token.BITNOT: childType = getJSType(n.getFirstChild()); if (!childType.matchesInt32Context()) { report(t, n, BIT_OPERATION, NodeUtil.opToStr(n.getType()), childType.toString()); } ensureTyped(t, n, NUMBER_TYPE); break; case Token.POS: case Token.NEG: left = n.getFirstChild(); validator.expectNumber(t, left, getJSType(left), "sign operator"); ensureTyped(t, n, NUMBER_TYPE); break; case Token.EQ: case Token.NE: { leftType = getJSType(n.getFirstChild()); rightType = getJSType(n.getLastChild()); JSType leftTypeRestricted = leftType.restrictByNotNullOrUndefined(); JSType rightTypeRestricted = rightType.restrictByNotNullOrUndefined(); TernaryValue result = leftTypeRestricted.testForEquality(rightTypeRestricted); if (result != TernaryValue.UNKNOWN) { if (n.getType() == Token.NE) { result = result.not(); } report(t, n, DETERMINISTIC_TEST, leftType.toString(), rightType.toString(), result.toString()); } ensureTyped(t, n, BOOLEAN_TYPE); break; } case Token.SHEQ: case Token.SHNE: { leftType = getJSType(n.getFirstChild()); rightType = getJSType(n.getLastChild()); JSType leftTypeRestricted = leftType.restrictByNotNullOrUndefined(); JSType rightTypeRestricted = rightType.restrictByNotNullOrUndefined(); if (!leftTypeRestricted.canTestForShallowEqualityWith( rightTypeRestricted)) { report(t, n, DETERMINISTIC_TEST_NO_RESULT, leftType.toString(), right

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS>Type.toString()); } ensureTyped(t, n, BOOLEAN_TYPE); break; } case Token.LT: case Token.LE: case Token.GT: case Token.GE: leftType = getJSType(n.getFirstChild()); rightType = getJSType(n.getLastChild()); if (rightType.isNumber()) { validator.expectNumber( t, n, leftType, "left side of numeric comparison"); } else if (leftType.isNumber()) { validator.expectNumber( t, n, rightType, "right side of numeric comparison"); } else if (leftType.matchesNumberContext() && rightType.matchesNumberContext()) { // OK. } else { // Whether the comparison is numeric will be determined at runtime // each time the expression is evaluated. Regardless, both operands // should match a string context. String message = "left side of comparison"; validator.expectString(t, n, leftType, message); validator.expectNotNullOrUndefined( t, n, leftType, message, getNativeType(STRING_TYPE)); message = "right side of comparison"; validator.expectString(t, n, rightType, message); validator.expectNotNullOrUndefined( t, n, rightType, message, getNativeType(STRING_TYPE)); } ensureTyped(t, n, BOOLEAN_TYPE); break; case Token.IN: left = n.getFirstChild(); right = n.getLastChild(); leftType = getJSType(left); rightType = getJSType(right); validator.expectObject(t, n, rightType, "'in' requires an object"); validator.expectString(t, left, leftType, "left side of 'in'"); ensureTyped(t, n, BOOLEAN_TYPE); break; case Token.INSTANCEOF: left = n.getFirstChild(); right = n.getLastChild(); leftType = getJSType(left); rightType = getJSType(right).restrictByNotNullOrUndefined(); validator.expectAnyObject( t, left, leftType, "deterministic instanceof yields false"); validator.expectActualObject( t, right, rightType, "instanceof requires an object");

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS> object type it is referring to. * @param t the traversal * @param assign the assign node * (<code>assign.getType() == Token.ASSIGN</code> is an implicit invariant) */ private void visitAssign(NodeTraversal t, Node assign) { JSDocInfo info = assign.getJSDocInfo(); Node lvalue = assign.getFirstChild(); Node rvalue = assign.getLastChild(); if (lvalue.getType() == Token.GETPROP) { Node object = lvalue.getFirstChild(); JSType objectJsType = getJSType(object); String property = lvalue.getLastChild().getString(); // the first name in this getprop refers to an interface // we perform checks in addition to the ones below if (object.getType() == Token.GETPROP) { JSType jsType = getJSType(object.getFirstChild()); if (jsType.isInterface() && object.getLastChild().getString().equals("prototype")) { visitInterfaceGetprop(t, assign, object, property, lvalue, rvalue); } } // /** @type ... */object.name = ...; if (info != null && info.hasType()) { visitAnnotatedAssignGetprop(t, assign, info.getType().evaluate(t.getScope(), typeRegistry), object, property, rvalue); return; } // /** @enum ... */object.name = ...; if (info != null && info.hasEnumParameterType()) { checkEnumInitializer( t, rvalue, info.getEnumParameterType().evaluate( t.getScope(), typeRegistry)); return; } // object.prototype = ...; if (property.equals("prototype")) { if (objectJsType instanceof FunctionType) { FunctionType functionType = (FunctionType) objectJsType; if (functionType.isConstructor()) { JSType rvalueType = rvalue.getJSType(); validator.expectObject(t, rvalue, rvalueType, OVERRIDING_PROTOTYPE_WITH_NON_OBJECT); } } else { // TODO(user): might want to flag that } return; } // object.prototype.property = ...; if (object.getType() == Token.GETPROP

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS>) { Node object2 = object.getFirstChild(); String property2 = NodeUtil.getStringValue(object.getLastChild()); if ("prototype".equals(property2)) { JSType jsType = object2.getJSType(); if (jsType instanceof FunctionType) { FunctionType functionType = (FunctionType) jsType; if (functionType.isConstructor() || functionType.isInterface()) { checkDeclaredPropertyInheritance( t, assign, functionType, property, info, getJSType(rvalue)); } } else { // TODO(user): might want to flag that } return; } } // object.property = ...; ObjectType type = ObjectType.cast( objectJsType.restrictByNotNullOrUndefined()); if (type != null) { if (type.hasProperty(property) && !type.isPropertyTypeInferred(property) && !propertyIsImplicitCast(type, property)) { validator.expectCanAssignToPropertyOf( t, assign, getJSType(rvalue), type.getPropertyType(property), object, property); } return; } } else if (lvalue.getType() == Token.NAME) { // variable with inferred type case JSType rvalueType = getJSType(assign.getLastChild()); Var var = t.getScope().getVar(lvalue.getString()); if (var != null) { if (var.isTypeInferred()) { return; } } } // fall through case JSType leftType = getJSType(lvalue); Node rightChild = assign.getLastChild(); JSType rightType = getJSType(rightChild); if (validator.expectCanAssignTo( t, assign, rightType, leftType, "assignment")) { ensureTyped(t, assign, rightType); } else { ensureTyped(t, assign); } } /** * Visits an object literal field definition <code>key : value</code>. * * If the <code>lvalue</code> is a prototype modification, we change the * schema of the object type it is referring to. * * @param t the traversal * @param key the assign node */ private void visitObjLitKey(

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS>NodeTraversal t, Node key, Node objlit) { // TODO(johnlenz): Validate get and set function declarations are valid // as is the functions can have "extraneous" bits. // For getter and setter property definitions the // rvalue type != the property type. Node rvalue = key.getFirstChild(); JSType rightType = NodeUtil.getObjectLitKeyTypeFromValueType( key, getJSType(rvalue)); if (rightType == null) { rightType = getNativeType(UNKNOWN_TYPE); } Node owner = objlit; // Validate value is assignable to the key type. JSType keyType = getJSType(key); boolean valid = validator.expectCanAssignToPropertyOf(t, key, rightType, keyType, owner, NodeUtil.getObjectLitKeyName(key)); if (valid) { ensureTyped(t, key, rightType); } else { ensureTyped(t, key); } // Validate that the key type is assignable to the object property type. // This is necessary as the objlit may have been cast to a non-literal // object type. // TODO(johnlenz): consider introducing a CAST node to the AST (or // perhaps a parentheses node). JSType objlitType = getJSType(objlit); ObjectType type = ObjectType.cast( objlitType.restrictByNotNullOrUndefined()); if (type != null) { String property = NodeUtil.getObjectLitKeyName(key); if (type.hasProperty(property) && !type.isPropertyTypeInferred(property) && !propertyIsImplicitCast(type, property)) { validator.expectCanAssignToPropertyOf( t, key, keyType, type.getPropertyType(property), owner, property); } return; } } /** * Returns true if any type in the chain has an implictCast annotation for * the given property. */ private boolean propertyIsImplicitCast(ObjectType type, String prop) { for (; type != null; type = type.getImplicitPrototype()) { JSDocInfo docInfo = type.getOwnPropertyJSDocInfo(prop); if (docInfo != null && docInfo.isImplicitCast()) { return true; } }

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS> return false; } /** * Given a constructor type and a property name, check that the property has * the JSDoc annotation @override iff the property is declared on a * superclass. Several checks regarding inheritance correctness are also * performed. */ private void checkDeclaredPropertyInheritance( NodeTraversal t, Node n, FunctionType ctorType, String propertyName, JSDocInfo info, JSType propertyType) { // If the supertype doesn't resolve correctly, we've warned about this // already. if (hasUnknownOrEmptySupertype(ctorType)) { return; } FunctionType superClass = ctorType.getSuperClassConstructor(); boolean superClassHasProperty = superClass != null && superClass.getPrototype().hasProperty(propertyName); boolean declaredOverride = info != null && info.isOverride(); boolean foundInterfaceProperty = false; if (ctorType.isConstructor()) { for (JSType implementedInterface : ctorType.getImplementedInterfaces()) { if (implementedInterface.isUnknownType() || implementedInterface.isEmptyType()) { continue; } FunctionType interfaceType = implementedInterface.toObjectType().getConstructor(); Preconditions.checkNotNull(interfaceType); boolean interfaceHasProperty = interfaceType.getPrototype().hasProperty(propertyName); foundInterfaceProperty = foundInterfaceProperty || interfaceHasProperty; if (reportMissingOverride.isOn() && !declaredOverride && interfaceHasProperty) { // @override not present, but the property does override an interface // property compiler.report(t.makeError(n, reportMissingOverride, HIDDEN_INTERFACE_PROPERTY, propertyName, interfaceType.getTopMostDefiningType(propertyName).toString())); } } } if (!declaredOverride && !superClassHasProperty) { // nothing to do here, it's just a plain new property return; } JSType topInstanceType = superClassHasProperty ? superClass.getTopMostDefiningType(propertyName) : null; if (reportMissingOverride.isOn() && ctorType.isConstructor() && !declaredOverride && superClassHasProperty) { // @override not present, but the property does override a superclass // property compiler.report(t.makeError(n, reportMissingOverride, HIDDEN_SUPERCLASS_PROPERTY, propertyName

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS>, topInstanceType.toString())); } if (!declaredOverride) { // there's no @override to check return; } // @override is present and we have to check that it is ok if (superClassHasProperty) { // there is a superclass implementation JSType superClassPropType = superClass.getPrototype().getPropertyType(propertyName); if (!propertyType.canAssignTo(superClassPropType)) { compiler.report( t.makeError(n, HIDDEN_SUPERCLASS_PROPERTY_MISMATCH, propertyName, topInstanceType.toString(), superClassPropType.toString(), propertyType.toString())); } } else if (!foundInterfaceProperty) { // there is no superclass nor interface implementation compiler.report( t.makeError(n, UNKNOWN_OVERRIDE, propertyName, ctorType.getInstanceType().toString())); } } /** * Given a constructor or an interface type, find out whether the unknown * type is a supertype of the current type. */ private static boolean hasUnknownOrEmptySupertype(FunctionType ctor) { Preconditions.checkArgument(ctor.isConstructor() || ctor.isInterface()); Preconditions.checkArgument(!ctor.isUnknownType()); // The type system should notice inheritance cycles on its own // and break the cycle. while (true) { ObjectType maybeSuperInstanceType = ctor.getPrototype().getImplicitPrototype(); if (maybeSuperInstanceType == null) { return false; } if (maybeSuperInstanceType.isUnknownType() || maybeSuperInstanceType.isEmptyType()) { return true; } ctor = maybeSuperInstanceType.getConstructor(); if (ctor == null) { return false; } Preconditions.checkState(ctor.isConstructor() || ctor.isInterface()); } } /** * Visits an ASSIGN node for cases such as * <pre> * interface.property2.property = ...; * </pre> */ private void visitInterfaceGetprop(NodeTraversal t, Node assign, Node object, String property, Node lvalue, Node rvalue) { JSType rvalueType = getJSType(rvalue); // Only 2 values are allowed for methods: // goog.abstractMethod // function () {}; //

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS> * * @param t The node traversal object that supplies context, such as the * scope chain to use in name lookups as well as error reporting. * @param n The node being visited. */ private void visitGetElem(NodeTraversal t, Node n) { Node left = n.getFirstChild(); Node right = n.getLastChild(); validator.expectIndexMatch(t, n, getJSType(left), getJSType(right)); ensureTyped(t, n); } /** * Visits a VAR node. * * @param t The node traversal object that supplies context, such as the * scope chain to use in name lookups as well as error reporting. * @param n The node being visited. */ private void visitVar(NodeTraversal t, Node n) { // TODO(nicksantos): Fix this so that the doc info always shows up // on the NAME node. We probably want to wait for the parser // merge to fix this. JSDocInfo varInfo = n.hasOneChild() ? n.getJSDocInfo() : null; for (Node name : n.children()) { Node value = name.getFirstChild(); // A null var would indicate a bug in the scope creation logic. Var var = t.getScope().getVar(name.getString()); if (value != null) { JSType valueType = getJSType(value); JSType nameType = var.getType(); nameType = (nameType == null) ? getNativeType(UNKNOWN_TYPE) : nameType; JSDocInfo info = name.getJSDocInfo(); if (info == null) { info = varInfo; } if (info != null && info.hasEnumParameterType()) { // var.getType() can never be null, this would indicate a bug in the // scope creation logic. checkEnumInitializer( t, value, info.getEnumParameterType().evaluate(t.getScope(), typeRegistry)); } else if (var.isTypeInferred()) { ensureTyped(t, name, valueType); } else { validator.expectCanAssignTo( t, value, valueType, nameType, "initializing variable"); } } } }

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS> /** * Visits a NEW node. */ private void visitNew(NodeTraversal t, Node n) { Node constructor = n.getFirstChild(); FunctionType type = getFunctionType(constructor); if (type != null && type.isConstructor()) { visitParameterList(t, n, type); ensureTyped(t, n, type.getInstanceType()); } else { // TODO(user): add support for namespaced objects. if (constructor.getType() != Token.GETPROP) { // TODO(user): make the constructor node have lineno/charno // and use constructor for a more precise error indication. // It seems that GETPROP nodes are missing this information. Node line; if (constructor.getLineno() < 0 || constructor.getCharno() < 0) { line = n; } else { line = constructor; } report(t, line, NOT_A_CONSTRUCTOR); } ensureTyped(t, n); } } /** * Visits a {@link Token#FUNCTION} node. * * @param t The node traversal object that supplies context, such as the * scope chain to use in name lookups as well as error reporting. * @param n The node being visited. */ private void visitFunction(NodeTraversal t, Node n) { JSDocInfo info = n.getJSDocInfo(); FunctionType functionType = (FunctionType) n.getJSType(); String functionPrivateName = n.getFirstChild().getString(); if (functionType.isInterface() || functionType.isConstructor()) { FunctionType baseConstructor = functionType. getPrototype().getImplicitPrototype().getConstructor(); if (baseConstructor != null && baseConstructor != getNativeType(OBJECT_FUNCTION_TYPE) && (baseConstructor.isConstructor() && functionType.isInterface() || baseConstructor.isInterface() && functionType.isConstructor())) { compiler.report( t.makeError(n, CONFLICTING_EXTENDED_TYPE, functionPrivateName)); } for (JSType baseInterface : functionType.getImplementedInterfaces()) { boolean badImplementedType = false; ObjectType baseInterfaceObj = ObjectType.cast(baseInterface); if (baseInterfaceObj != null

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS>) { FunctionType interfaceConstructor = baseInterfaceObj.getConstructor(); if (interfaceConstructor != null && !interfaceConstructor.isInterface()) { badImplementedType = true; } } else { badImplementedType = true; } if (badImplementedType) { report(t, n, BAD_IMPLEMENTED_TYPE, functionPrivateName); } } if (functionType.isConstructor()) { validator.expectAllInterfaceProperties(t, n, functionType); } } } /** * Visits a CALL node. * * @param t The node traversal object that supplies context, such as the * scope chain to use in name lookups as well as error reporting. * @param n The node being visited. */ private void visitCall(NodeTraversal t, Node n) { Node child = n.getFirstChild(); JSType childType = getJSType(child).restrictByNotNullOrUndefined(); if (!childType.canBeCalled()) { report(t, n, NOT_CALLABLE, childType.toString()); ensureTyped(t, n); return; } // A couple of types can be called as if they were functions. // If it is a function type, then validate parameters. if (childType instanceof FunctionType) { FunctionType functionType = (FunctionType) childType; boolean isExtern = false; JSDocInfo functionJSDocInfo = functionType.getJSDocInfo(); if(functionJSDocInfo != null) { String sourceName = functionJSDocInfo.getSourceName(); CompilerInput functionSource = compiler.getInput(sourceName); isExtern = functionSource.isExtern(); } // Non-native constructors should not be called directly // unless they specify a return type and are defined // in an extern. if (functionType.isConstructor() && !functionType.isNativeObjectType() && (functionType.getReturnType().isUnknownType() || functionType.getReturnType().isVoidType() || !isExtern)) { report(t, n, CONSTRUCTOR_NOT_CALLABLE, childType.toString()); } visitParameterList(t, n, functionType); ensureTyped(t, n, functionType

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS> it go. if (function == null) { return; } JSType jsType = getJSType(function); if (jsType instanceof FunctionType) { FunctionType functionType = (FunctionType) jsType; JSType returnType = functionType.getReturnType(); // if no return type is specified, undefined must be returned // (it's a void function) if (returnType == null) { returnType = getNativeType(VOID_TYPE); } // fetching the returned value's type Node valueNode = n.getFirstChild(); JSType actualReturnType; if (valueNode == null) { actualReturnType = getNativeType(VOID_TYPE); valueNode = n; } else { actualReturnType = getJSType(valueNode); } // verifying validator.expectCanAssignTo(t, valueNode, actualReturnType, returnType, "inconsistent return type"); } } /** * This function unifies the type checking involved in the core binary * operators and the corresponding assignment operators. The representation * used internally is such that common code can handle both kinds of * operators easily. * * @param op The operator. * @param t The traversal object, needed to report errors. * @param n The node being checked. */ private void visitBinaryOperator(int op, NodeTraversal t, Node n) { Node left = n.getFirstChild(); JSType leftType = getJSType(left); Node right = n.getLastChild(); JSType rightType = getJSType(right); switch (op) { case Token.ASSIGN_LSH: case Token.ASSIGN_RSH: case Token.LSH: case Token.RSH: case Token.ASSIGN_URSH: case Token.URSH: if (!leftType.matchesInt32Context()) { report(t, left, BIT_OPERATION, NodeUtil.opToStr(n.getType()), leftType.toString()); } if (!rightType.matchesUint32Context()) { report(t, right, BIT_OPERATION, NodeUtil.opToStr(n.getType()), rightType.toString()); } break; case Token.ASSIGN_DIV: case Token.ASSIGN_MOD: case

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS> Token.ASSIGN_MUL: case Token.ASSIGN_SUB: case Token.DIV: case Token.MOD: case Token.MUL: case Token.SUB: validator.expectNumber(t, left, leftType, "left operand"); validator.expectNumber(t, right, rightType, "right operand"); break; case Token.ASSIGN_BITAND: case Token.ASSIGN_BITXOR: case Token.ASSIGN_BITOR: case Token.BITAND: case Token.BITXOR: case Token.BITOR: validator.expectBitwiseable(t, left, leftType, "bad left operand to bitwise operator"); validator.expectBitwiseable(t, right, rightType, "bad right operand to bitwise operator"); break; case Token.ASSIGN_ADD: case Token.ADD: break; default: report(t, n, UNEXPECTED_TOKEN, Node.tokenToName(op)); } ensureTyped(t, n); } /** * <p>Checks the initializer of an enum. An enum can be initialized with an * object literal whose values must be subtypes of the declared enum element * type, or by copying another enum.</p> * * <p>In the case of an enum copy, we verify that the enum element type of the * enum used for initialization is a subtype of the enum element type of * the enum the value is being copied in.</p> * * <p>Examples:</p> * <pre>var myEnum = {FOO: ..., BAR: ...}; * var myEnum = myOtherEnum;</pre> * * @param value the value used for initialization of the enum * @param primitiveType The type of each element of the enum. */ private void checkEnumInitializer( NodeTraversal t, Node value, JSType primitiveType) { if (value.getType() == Token.OBJECTLIT) { for (Node key = value.getFirstChild(); key != null; key = key.getNext()) { Node propValue = key.getFirstChild(); // the value's type must be assignable to the enum's primitive type validator.expectCanAssignTo( t, propValue, getJSType(propValue), primitiveType, "

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS>element type must match enum's type"); } } else if (value.getJSType() instanceof EnumType) { // TODO(user): Remove the instanceof check in favor // of a type.isEnumType() predicate. Currently, not all enum types are // implemented by the EnumClass, e.g. the unknown type and the any // type. The types need to be defined by interfaces such that an // implementation can implement multiple types interface. EnumType valueEnumType = (EnumType) value.getJSType(); JSType valueEnumPrimitiveType = valueEnumType.getElementsType().getPrimitiveType(); validator.expectCanAssignTo(t, value, valueEnumPrimitiveType, primitiveType, "incompatible enum element types"); } else { // The error condition is handled in TypedScopeCreator. } } /** * This predicate is used to determine if the node represents an expression * that is a Reference according to JavaScript definitions. * * @param n The node being checked. * @return true if the sub-tree n is a reference, false otherwise. */ private static boolean isReference(Node n) { switch (n.getType()) { case Token.GETELEM: case Token.GETPROP: case Token.NAME: return true; default: return false; } } /** * This method gets the JSType from the Node argument and verifies that it is * present. */ private JSType getJSType(Node n) { JSType jsType = n.getJSType(); if (jsType == null) { // TODO(nicksantos): This branch indicates a compiler bug, not worthy of // halting the compilation but we should log this and analyze to track // down why it happens. This is not critical and will be resolved over // time as the type checker is extended. return getNativeType(UNKNOWN_TYPE); } else { return jsType; } } /** * Gets the type of the node or {@code null} if the node's type is not a * function. */ private FunctionType getFunctionType(Node n) { JSType type = getJSType(n).restrictByNotNullOrUndefined(); if (type.isUnknownType()) { return type

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS>Registry.getNativeFunctionType(U2U_CONSTRUCTOR_TYPE); } else if (type instanceof FunctionType) { return (FunctionType) type; } else { return null; } } // TODO(nicksantos): TypeCheck should never be attaching types to nodes. // All types should be attached by TypeInference. This is not true today // for legacy reasons. There are a number of places where TypeInference // doesn't attach a type, as a signal to TypeCheck that it needs to check // that node's type. /** * Ensure that the given node has a type. If it does not have one, * attach the UNKNOWN_TYPE. */ private void ensureTyped(NodeTraversal t, Node n) { ensureTyped(t, n, getNativeType(UNKNOWN_TYPE)); } private void ensureTyped(NodeTraversal t, Node n, JSTypeNative type) { ensureTyped(t, n, getNativeType(type)); } /** * Enforces type casts, and ensures the node is typed. * * A cast in the way that we use it in JSDoc annotations never * alters the generated code and therefore never can induce any runtime * operation. What this means is that a 'cast' is really just a compile * time constraint on the underlying value. In the future, we may add * support for run-time casts for compiled tests. * * To ensure some shred of sanity, we enforce the notion that the * type you are casting to may only meaningfully be a narrower type * than the underlying declared type. We also invalidate optimizations * on bad type casts. * * @param t The traversal object needed to report errors. * @param n The node getting a type assigned to it. * @param type The type to be assigned. */ private void ensureTyped(NodeTraversal t, Node n, JSType type) { // Make sure FUNCTION nodes always get function type. Preconditions.checkState(n.getType() != Token.FUNCTION || type instanceof FunctionType || type.isUnknownType()); JSDocInfo info = n.getJSDocInfo(); if (info != null) { if (info.hasType()) { JSType infoType =

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS> { return assignment.getLastChild(); } } /** * Represents member declarations using a object literal. * Example: var x = { e : function() { } }; */ static final class ObjectLiteralPropertyDefinition extends Definition { private final Node literal; private final Node name; private final Node value; ObjectLiteralPropertyDefinition(Node lit, Node name, Node value, boolean isExtern) { super(isExtern); this.literal = lit; this.name = name; this.value = value; } @Override public void performRemove() { literal.removeChild(name); } @Override public Node getLValue() { // TODO(user) revisit: object literal definitions are an example // of definitions whose lhs doesn't correspond to a node that // exists in the AST. We will have to change the return type of // getLValue sooner or later in order to provide this added // flexibility. switch (name.getType()) { case Token.SET: case Token.GET: case Token.STRING: // TODO(johnlenz): return a GETELEM for quoted strings. return new Node(Token.GETPROP, new Node(Token.OBJECTLIT), name.cloneNode()); case Token.NUMBER: return new Node(Token.GETELEM, new Node(Token.OBJECTLIT), name.cloneNode()); default: throw new IllegalStateException("unexpected"); } } @Override public Node getRValue() { return value; } } /** * Represents a VAR declaration with an assignment. */ static final class VarDefinition extends Definition { private final Node name; VarDefinition(Node node, boolean inExterns) { super(inExterns); Preconditions.checkArgument(NodeUtil.isVarDeclaration(node)); Preconditions.checkArgument(node.hasChildren(), "VAR Declaration of " + node.getString() + "should be assigned a value."); name = node; } @Override public void performRemove() { Node var = name.getParent(); Preconditions.checkState(var.getFirstChild() == var.getLastChild(), "AST should be normalized first"); Node parent = var.getParent(); Node rValue = name.

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS> properties of this type are also NoResolved types, * and comparisons to other types always have an unknown result. * * @author nicksantos@google.com (Nick Santos) */ class NoResolvedType extends NoType { private static final long serialVersionUID = 1L; NoResolvedType(JSTypeRegistry registry) { super(registry); } @Override public boolean isNoResolvedType() { return true; } @Override public boolean isNoType() { return false; } @Override public JSType getPropertyType(String propertyName) { return getNativeType(JSTypeNative.CHECKED_UNKNOWN_TYPE); } @Override public boolean isSubtype(JSType that) { if (JSType.isSubtype(this, that)) { return true; } else { return !that.isNoType(); } } @Override public String toString() { return "NoResolvedType"; } }

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS> source = getSource(); String sourceExcerpt = source == null ? null : excerpt.get( source, error.sourceName, error.lineNumber, excerptFormatter); // formatting the message StringBuilder b = new StringBuilder(); if (error.sourceName != null) { b.append(error.sourceName); if (error.lineNumber > 0) { b.append(':'); b.append(error.lineNumber); } b.append(": "); } b.append(getLevelName(warning ? CheckLevel.WARNING : CheckLevel.ERROR)); b.append(" - "); b.append(error.description); b.append('\n'); if (sourceExcerpt != null) { b.append(sourceExcerpt); b.append('\n'); int charno = error.getCharno(); // padding equal to the excerpt and arrow at the end if (excerpt.equals(LINE) && 0 <= charno && charno < sourceExcerpt.length()) { for (int i = 0; i < charno; i++) { char c = sourceExcerpt.charAt(i); if (Character.isWhitespace(c)) { b.append(c); } else { b.append(' '); } } b.append("^\n"); } } return b.toString(); } /** * Formats a region by appending line numbers in front, e.g. * <pre> 9| if (foo) { * 10| alert('bar'); * 11| }</pre> * and return line excerpt without any modification. */ static class LineNumberingFormatter implements ExcerptFormatter { public String formatLine(String line, int lineNumber) { return line; } public String formatRegion(Region region) { if (region == null) { return null; } String code = region.getSourceExcerpt(); if (code.length() == 0) { return null; } // max length of the number display int numberLength = Integer.toString(region.getEndingLineNumber()) .length(); // formatting StringBuilder builder = new StringBuilder(code.length() * 2); int

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS> start = 0; int end = code.indexOf('\n', start); int lineNumber = region.getBeginningLineNumber(); while (start >= 0) { // line extraction String line; if (end < 0) { line = code.substring(start); if (line.length() == 0) { return builder.substring(0, builder.length() - 1); } } else { line = code.substring(start, end); } builder.append(" "); // nice spaces for the line number int spaces = numberLength - Integer.toString(lineNumber).length(); builder.append(Strings.repeat(" ", spaces)); builder.append(lineNumber); builder.append("| "); // end & update if (end < 0) { builder.append(line); start = -1; } else { builder.append(line); builder.append('\n'); start = end + 1; end = code.indexOf('\n', start); lineNumber++; } } return builder.toString(); } } }

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS>Prototype(); if (implicitPrototype != null) { return implicitPrototype.isPropertyTypeInferred(property); } // property does not exist return false; } return p.inferred; } @Override public JSType getPropertyType(String propertyName) { Property p = properties.get(propertyName); if (p != null) { return p.type; } ObjectType implicitPrototype = getImplicitPrototype(); if (implicitPrototype != null) { return implicitPrototype.getPropertyType(propertyName); } return getNativeType(JSTypeNative.UNKNOWN_TYPE); } @Override public boolean isPropertyInExterns(String propertyName) { Property p = properties.get(propertyName); if (p != null) { return p.inExterns; } ObjectType implicitPrototype = getImplicitPrototype(); if (implicitPrototype != null) { return implicitPrototype.isPropertyInExterns(propertyName); } return false; } @Override boolean defineProperty(String name, JSType type, boolean inferred, boolean inExterns, Node propertyNode) { if (hasOwnDeclaredProperty(name)) { return false; } properties.put(name, new Property(type, inferred, inExterns, propertyNode)); return true; } @Override public Node getPropertyNode(String propertyName) { Property p = properties.get(propertyName); if (p != null) { return p.propertyNode; } ObjectType implicitPrototype = getImplicitPrototype(); if (implicitPrototype != null) { return implicitPrototype.getPropertyNode(propertyName); } return null; } @Override public JSDocInfo getOwnPropertyJSDocInfo(String propertyName) { Property p = properties.get(propertyName); if (p != null) { return p.docInfo; } return null; } @Override public void setPropertyJSDocInfo(String propertyName, JSDocInfo info, boolean inExterns) { if (info != null) { if (!properties.containsKey(propertyName)) { // If docInfo was attached, but the type of the property // was not defined anywhere, then we consider this an explicit // declaration of the property. defineInferredProperty

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS>(propertyName, getPropertyType(propertyName), inExterns, null); } // The prototype property is not represented as a normal Property. // We probably don't want to attach any JSDoc to it anyway. Property property = properties.get(propertyName); if (property != null) { property.docInfo = info; } } } @Override public boolean matchesNumberContext() { return isNumberObjectType() || isDateType() || isBooleanObjectType() || isStringObjectType() || hasOverridenNativeProperty("valueOf"); } @Override public boolean matchesStringContext() { return isTheObjectType() || isStringObjectType() || isDateType() || isRegexpType() || isArrayType() || isNumberObjectType() || isBooleanObjectType() || hasOverridenNativeProperty("toString"); } /** * Given the name of a native object property, checks whether the property is * present on the object and different from the native one. */ private boolean hasOverridenNativeProperty(String propertyName) { if (isNative()) { return false; } JSType propertyType = getPropertyType(propertyName); ObjectType nativeType = this.isFunctionType() ? registry.getNativeObjectType(JSTypeNative.FUNCTION_PROTOTYPE) : registry.getNativeObjectType(JSTypeNative.OBJECT_PROTOTYPE); JSType nativePropertyType = nativeType.getPropertyType(propertyName); return propertyType != nativePropertyType; } @Override public JSType unboxesTo() { if (isStringObjectType()) { return getNativeType(JSTypeNative.STRING_TYPE); } else if (isBooleanObjectType()) { return getNativeType(JSTypeNative.BOOLEAN_TYPE); } else if (isNumberObjectType()) { return getNativeType(JSTypeNative.NUMBER_TYPE); } else { return super.unboxesTo(); } } @Override public boolean matchesObjectContext() { return true; } @Override public boolean canBeCalled() { return isRegexpType(); } /** * Whether this represents a native type (such as Object, Date, * RegExp, etc.). */ boolean isNative() { return nativeType; } @Override public String toString() { if (

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS>hasReferenceName()) { return getReferenceName(); } else if (prettyPrint) { // Use a tree set so that the properties are sorted. Set<String> propertyNames = Sets.newTreeSet(); for (ObjectType current = this; current != null && !current.isNativeObjectType() && propertyNames.size() <= MAX_PRETTY_PRINTED_PROPERTIES; current = current.getImplicitPrototype()) { propertyNames.addAll(current.getOwnPropertyNames()); } StringBuilder sb = new StringBuilder(); sb.append("{"); int i = 0; for (String property : propertyNames) { if (i > 0) { sb.append(", "); } sb.append(property); sb.append(": "); sb.append(getPropertyType(property).toString()); ++i; if (i == MAX_PRETTY_PRINTED_PROPERTIES) { sb.append(", ..."); break; } } sb.append("}"); return sb.toString(); } else { return "{...}"; } } void setPrettyPrint(boolean prettyPrint) { this.prettyPrint = prettyPrint; } @Override public FunctionType getConstructor() { return null; } @Override public ObjectType getImplicitPrototype() { return implicitPrototypeFallback; } /** * This should only be reset on the FunctionPrototypeType, only to fix an * incorrectly established prototype chain due to the user having a mismatch * in super class declaration, and only before properties on that type are * processed. */ final void setImplicitPrototype(ObjectType implicitPrototype) { checkState(!hasCachedValues()); this.implicitPrototypeFallback = implicitPrototype; } @Override public String getReferenceName() { if (className != null) { return className; } else { return null; } } @Override public boolean hasReferenceName() { return className != null; } @Override public boolean isSubtype(JSType that) { if (JSType.isSubtype(this, that)) { return true; } // Union types if (that instanceof UnionType) { // The static {@code JSType.isSubtype} check already decomposed // union types, so we don't need

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS> to check those again. return false; } // record types if (that instanceof RecordType) { return RecordType.isSubtype(this, (RecordType) that); } // Interfaces // Find all the interfaces implemented by this class and compare each one // to the interface instance. ObjectType thatObj = that.toObjectType(); ObjectType thatCtor = thatObj == null ? null : thatObj.getConstructor(); if (thatCtor != null && thatCtor.isInterface()) { Iterable<ObjectType> thisInterfaces = getCtorImplementedInterfaces(); for (ObjectType thisInterface : thisInterfaces) { if (thisInterface.isSubtype(that)) { return true; } } } // other prototype based objects if (that != null) { if (isUnknownType() || implicitPrototypeChainIsUnknown()) { // If unsure, say 'yes', to avoid spurious warnings. // TODO(user): resolve the prototype chain completely in all cases, // to avoid guessing. return true; } return this.isImplicitPrototype(thatObj); } return false; } private boolean implicitPrototypeChainIsUnknown() { ObjectType p = getImplicitPrototype(); while (p != null) { if (p.isUnknownType()) { return true; } p = p.getImplicitPrototype(); } return false; } private static final class Property implements Serializable { private static final long serialVersionUID = 1L; /** * Property's type. */ private JSType type; /** * Whether the property's type is inferred. */ private final boolean inferred; /** * Whether the property is defined in the externs. */ private final boolean inExterns; /** * The node corresponding to this property, e.g., a GETPROP node that * declares this property. */ private final Node propertyNode; /** The JSDocInfo for this property. */ private JSDocInfo docInfo = null; private Property(JSType type, boolean inferred, boolean inExterns, Node propertyNode) { this.type = type; this.inferred = inferred; this.inExterns = inExterns; this.propertyNode = propertyNode; }

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS> */ void endPass() { Preconditions.checkState(currentTracer != null, "Tracer should not be null at the end of a pass."); stopTracer(currentTracer, currentPassName); String passToCheck = currentPassName; currentPassName = null; currentTracer = null; maybeSanityCheck(); } /** * Returns a new tracer for the given pass name. */ Tracer newTracer(String passName) { String comment = passName + (recentChange.hasCodeChanged() ? " on recently changed AST" : ""); if (options.tracer.isOn()) { tracker.recordPassStart(passName); } return new Tracer("Compiler", comment); } void stopTracer(Tracer t, String passName) { long result = t.stop(); if (options.tracer.isOn()) { tracker.recordPassStop(passName, result); } } /** * Returns the result of the compilation. */ public Result getResult() { PassConfig.State state = getPassConfig().getIntermediateState(); return new Result(getErrors(), getWarnings(), debugLog.toString(), state.variableMap, state.propertyMap, state.anonymousFunctionNameMap, state.stringMap, functionInformationMap, sourceMap, externExports, state.cssNames, state.idGeneratorMap); } /** * Returns an array constructed from errors + temporary warnings. */ public JSError[] getMessages() { return getErrors(); } /** * Returns the array of errors (never null). */ public JSError[] getErrors() { return errorManager.getErrors(); } /** * Returns the array of warnings (never null). */ public JSError[] getWarnings() { return errorManager.getWarnings(); } /** * Returns the root node of the AST, which includes both externs and source. */ public Node getRoot() { return externAndJsRoot; } /** * Creates a new id for making unique names. */ private int nextUniqueNameId() { return uniqueNameId++; } /** * Resets the unique name id counter */ @VisibleForTesting void resetUniqueNameId() { uniqueNameId = 0;

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS> } // Inputs can have a null AST during initial parse. if (n == null) { continue; } if (n.getJSDocInfo() != null) { JSDocInfo info = n.getJSDocInfo(); if (info.isExterns()) { // If the input file is explicitly marked as an externs file, then // assume the programmer made a mistake and throw it into // the externs pile anyways. externsRoot.addChildToBack(n); input.setIsExtern(true); input.getModule().remove(input); externs.add(input); staleInputs = true; } else if (info.isNoCompile()) { input.getModule().remove(input); staleInputs = true; } } } if (staleInputs) { fillEmptyModules(modules); rebuildInputsFromModules(); } // Build the AST. for (CompilerInput input : inputs) { Node n = input.getAstRoot(this); if (n == null) { continue; } if (devMode) { runSanityCheck(); if (hasErrors()) { return null; } } if (options.sourceMapOutputPath != null || options.nameReferenceReportPath != null) { // Annotate the nodes in the tree with information from the // input file. This information is used to construct the SourceMap. SourceInformationAnnotator sia = new SourceInformationAnnotator( input.getName(), options.devMode != DevMode.OFF); NodeTraversal.traverse(this, n, sia); } jsRoot.addChildToBack(n); } return externAndJsRoot; } finally { stopTracer(tracer, "parseInputs"); } } public Node parse(JSSourceFile file) { initCompilerOptionsIfTesting(); addToDebugLog("Parsing: " + file.getName()); return new JsAst(file).getAstRoot(this); } @Override Node parseSyntheticCode(String js) { CompilerInput input = new CompilerInput( JSSourceFile.fromCode(" [synthetic] ", js)); inputsByName.put(input.getName(), input); return input.get

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS>AstRoot(this); } void initCompilerOptionsIfTesting() { if (options == null) { // initialization for tests that don't initialize the compiler // by the normal mechanisms. initOptions(new CompilerOptions()); } } @Override Node parseSyntheticCode(String fileName, String js) { initCompilerOptionsIfTesting(); return parse(JSSourceFile.fromCode(fileName, js)); } @Override Node parseTestCode(String js) { initCompilerOptionsIfTesting(); CompilerInput input = new CompilerInput( JSSourceFile.fromCode(" [testcode] ", js)); if (inputsByName == null) { inputsByName = Maps.newHashMap(); } inputsByName.put(input.getName(), input); return input.getAstRoot(this); } @Override ErrorReporter getDefaultErrorReporter() { return defaultErrorReporter; } //------------------------------------------------------------------------ // Convert back to source code //------------------------------------------------------------------------ /** * Converts the main parse tree back to js code. */ public String toSource() { return runInCompilerThread(new Callable<String>() { public String call() throws Exception { Tracer tracer = newTracer("toSource"); try { CodeBuilder cb = new CodeBuilder(); if (jsRoot != null) { int i = 0; for (Node scriptNode = jsRoot.getFirstChild(); scriptNode != null; scriptNode = scriptNode.getNext()) { toSource(cb, i++, scriptNode); } } return cb.toString(); } finally { stopTracer(tracer, "toSource"); } } }); } /** * Converts the parse tree for each input back to js code. */ public String[] toSourceArray() { return runInCompilerThread(new Callable<String[]>() { public String[] call() throws Exception { Tracer tracer = newTracer("toSourceArray"); try { int numInputs = inputs.size(); String[] sources = new String[numInputs]; CodeBuilder cb = new CodeBuilder(); for (int i = 0; i < numInputs; i++) { Node scriptNode = inputs.get(i).getAstRoot(Compiler.this); cb.reset();

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS> toSource(cb, i, scriptNode); sources[i] = cb.toString(); } return sources; } finally { stopTracer(tracer, "toSourceArray"); } } }); } /** * Converts the parse tree for a module back to js code. */ public String toSource(final JSModule module) { return runInCompilerThread(new Callable<String>() { public String call() throws Exception { List<CompilerInput> inputs = module.getInputs(); int numInputs = inputs.size(); if (numInputs == 0) { return ""; } CodeBuilder cb = new CodeBuilder(); for (int i = 0; i < numInputs; i++) { Node scriptNode = inputs.get(i).getAstRoot(Compiler.this); if (scriptNode == null) { throw new IllegalArgumentException( "Bad module: " + module.getName()); } toSource(cb, i, scriptNode); } return cb.toString(); } }); } /** * Converts the parse tree for each input in a module back to js code. */ public String[] toSourceArray(final JSModule module) { return runInCompilerThread(new Callable<String[]>() { public String[] call() throws Exception { List<CompilerInput> inputs = module.getInputs(); int numInputs = inputs.size(); if (numInputs == 0) { return new String[0]; } String[] sources = new String[numInputs]; CodeBuilder cb = new CodeBuilder(); for (int i = 0; i < numInputs; i++) { Node scriptNode = inputs.get(i).getAstRoot(Compiler.this); if (scriptNode == null) { throw new IllegalArgumentException( "Bad module input: " + inputs.get(i).getName()); } cb.reset(); toSource(cb, i, scriptNode); sources[i] = cb.toString(); } return sources; } }); } /** * Writes out js code from a root node. If printing input delimiters, this * method will attach a comment to the start of the text indicating which * input the output derived from. If there were any preserve annotations

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS> * within the root's source, they will also be printed in a block comment * at the beginning of the output. */ public void toSource(final CodeBuilder cb, final int inputSeqNum, final Node root) { runInCompilerThread(new Callable<Void>() { public Void call() throws Exception { if (options.printInputDelimiter) { if ((cb.getLength() > 0) && !cb.endsWith("\n")) { cb.append("\n"); // Make sure that the label starts on a new line } Preconditions.checkState(root.getType() == Token.SCRIPT); String delimiter = options.inputDelimiter; String sourceName = (String)root.getProp(Node.SOURCENAME_PROP); Preconditions.checkState(sourceName != null); Preconditions.checkState(!sourceName.isEmpty()); delimiter = delimiter.replaceAll("%name%", sourceName) .replaceAll("%num%", String.valueOf(inputSeqNum)); cb.append(delimiter) .append("\n"); } if (root.getJSDocInfo() != null && root.getJSDocInfo().getLicense() != null) { cb.append("/*\n") .append(root.getJSDocInfo().getLicense()) .append("*/\n"); } // If there is a valid source map, then indicate to it that the current // root node's mappings are offset by the given string builder buffer. if (options.sourceMapOutputPath != null) { sourceMap.setStartingPosition( cb.getLineIndex(), cb.getColumnIndex()); } String code = toSource(root, sourceMap); if (!code.isEmpty()) { cb.append(code); // In order to avoid parse ambiguity when files are concatenated // together, all files should end in a semi-colon. Do a quick // heuristic check if there's an obvious semi-colon already there. int length = code.length(); char lastChar = code.charAt(length - 1); char secondLastChar = length >= 2 ? code.charAt(length - 2) : '\0'; boolean hasSemiColon = lastChar == ';' || (lastChar == '\n' && secondLastChar == ';');

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS> if (!hasSemiColon) { cb.append(";"); } } return null; } }); } /** * Generates JavaScript source code for an AST, doesn't generate source * map info. */ @Override String toSource(Node n) { initCompilerOptionsIfTesting(); return toSource(n, null); } /** * Generates JavaScript source code for an AST. */ private String toSource(Node n, SourceMap sourceMap) { CodePrinter.Builder builder = new CodePrinter.Builder(n); builder.setPrettyPrint(options.prettyPrint); builder.setLineBreak(options.lineBreak); builder.setSourceMap(sourceMap); builder.setSourceMapDetailLevel(options.sourceMapDetailLevel); builder.setTagAsStrict( options.getLanguageOut() == LanguageMode.ECMASCRIPT5_STRICT); builder.setLineLengthThreshold(options.lineLengthThreshold); Charset charset = options.outputCharset != null ? Charset.forName(options.outputCharset) : null; builder.setOutputCharset(charset); return builder.build(); } /** * Stores a buffer of text to which more can be appended. This is just like a * StringBuilder except that we also track the number of lines. */ public static class CodeBuilder { private final StringBuilder sb = new StringBuilder(); private int lineCount = 0; private int colCount = 0; /** Removes all text, but leaves the line count unchanged. */ void reset() { sb.setLength(0); } /** Appends the given string to the text buffer. */ CodeBuilder append(String str) { sb.append(str); // Adjust the line and column information for the new text. int index = -1; int lastIndex = index; while ((index = str.indexOf('\n', index + 1)) >= 0) { ++lineCount; lastIndex = index; } if (lastIndex == -1) { // No new lines, append the new characters added. colCount += str.length(); } else { colCount = str.length() - (lastIndex + 1); } return this; } /** Returns all text

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS> in the text buffer. */ @Override public String toString() { return sb.toString(); } /** Returns the length of the text buffer. */ public int getLength() { return sb.length(); } /** Returns the (zero-based) index of the last line in the text buffer. */ int getLineIndex() { return lineCount; } /** Returns the (zero-based) index of the last column in the text buffer. */ int getColumnIndex() { return colCount; } /** Determines whether the text ends with the given suffix. */ boolean endsWith(String suffix) { return (sb.length() > suffix.length()) && suffix.equals(sb.substring(sb.length() - suffix.length())); } } //------------------------------------------------------------------------ // Optimizations //------------------------------------------------------------------------ public void optimize() { // Ideally, this pass should be the first pass run, however: // 1) VariableReferenceCheck reports unexpected warnings if Normalize // is done first. // 2) ReplaceMessages, stripCode, and potentially custom passes rely on // unmodified local names. normalize(); PhaseOptimizer phaseOptimizer = new PhaseOptimizer(this, tracker); if (options.devMode == DevMode.EVERY_PASS) { phaseOptimizer.setSanityCheck(sanityCheck); } phaseOptimizer.consume(getPassConfig().getOptimizations()); phaseOptimizer.process(externsRoot, jsRoot); if (hasErrors()) { return; } } @Override void setCssRenamingMap(CssRenamingMap map) { options.cssRenamingMap = map; } @Override CssRenamingMap getCssRenamingMap() { return options.cssRenamingMap; } /** * Reprocesses the current defines over the AST. This is used by GwtCompiler * to generate N outputs for different targets from the same (checked) AST. * For each target, we apply the target-specific defines by calling * {@code processDefines} and then {@code optimize} to optimize the AST * specifically for that target. */ public void processDefines() { (new DefaultPassConfig(options)).processDefines.create(this) .process(externsRoot, jsRoot

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS>details); } @Override public final String getMessage() { String details = details(); if (sourceName == null || lineNumber <= 0) { return details; } StringBuilder buf = new StringBuilder(details); buf.append(" ("); if (sourceName != null) { buf.append(sourceName); } if (lineNumber > 0) { buf.append('#'); buf.append(lineNumber); } buf.append(')'); return buf.toString(); } public String details() { return super.getMessage(); } /** * Get the uri of the script source containing the error, or null * if that information is not available. */ public final String sourceName() { return sourceName; } /** * Initialize the uri of the script source containing the error. * * @param sourceName the uri of the script source reponsible for the error. * It should not be <tt>null</tt>. * * @throws IllegalStateException if the method is called more then once. */ public final void initSourceName(String sourceName) { if (sourceName == null) throw new IllegalArgumentException(); if (this.sourceName != null) throw new IllegalStateException(); this.sourceName = sourceName; } /** * Returns the line number of the statement causing the error, * or zero if not available. */ public final int lineNumber() { return lineNumber; } /** * Initialize the line number of the script statement causing the error. * * @param lineNumber the line number in the script source. * It should be positive number. * * @throws IllegalStateException if the method is called more then once. */ public final void initLineNumber(int lineNumber) { if (lineNumber <= 0) throw new IllegalArgumentException(String.valueOf(lineNumber)); if (this.lineNumber > 0) throw new IllegalStateException(); this.lineNumber = lineNumber; } /** * The column number of the location of the error, or zero if unknown. */ public final int columnNumber() { return columnNumber; } /** * Initialize the column number of the script statement causing the error. * * @param columnNumber the

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS> { return name.endsWith(".js"); } }); } /** * Get a string representing the script stack of this exception. * If optimization is enabled, this corresponds to all java stack elements * with a source name matching the <code>filter</code>. * @param filter the file name filter to determine whether a file is a * script file * @return a script stack dump * @since 1.6R6 */ public String getScriptStackTrace(FilenameFilter filter) { // The real Rhino code here has been removed. return "<No stack trace available>"; } @Override public void printStackTrace(PrintWriter s) { if (interpreterStackInfo == null) { super.printStackTrace(s); } else { s.print(generateStackTrace()); } } @Override public void printStackTrace(PrintStream s) { if (interpreterStackInfo == null) { super.printStackTrace(s); } else { s.print(generateStackTrace()); } } private String sourceName; private int lineNumber; private String lineSource; private int columnNumber; Object interpreterStackInfo; int[] interpreterLineData; }

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS>rhino.Node; import java.util.Set; /** * Object type. * * In JavaScript, all object types have properties, and each of those * properties has a type. Property types may be DECLARED, INFERRED, or * UNKNOWN. * * DECLARED properties have an explicit type annotation, as in: * <code> * /xx @type {number} x/ * Foo.prototype.bar = 1; * </code> * This property may only hold number values, and an assignment to any * other type of value is an error. * * INFERRED properties do not have an explicit type annotation. Rather, * we try to find all the possible types that this property can hold. * <code> * Foo.prototype.bar = 1; * </code> * If the programmer assigns other types of values to this property, * the property will take on the union of all these types. * * UNKNOWN properties are properties on the UNKNOWN type. The UNKNOWN * type has all properties, but we do not know whether they are * declared or inferred. * */ public abstract class ObjectType extends JSType { private boolean visited; private JSDocInfo docInfo = null; private boolean unknown = true; ObjectType(JSTypeRegistry registry) { super(registry); } /** * Gets the declared default element type. * @see ParameterizedType */ public JSType getParameterType() { return null; } /** * Gets the declared default index type. * @see IndexedType */ public JSType getIndexType() { return null; } /** * Gets the docInfo for this type. */ @Override public JSDocInfo getJSDocInfo() { if (docInfo != null) { return docInfo; } else if (getImplicitPrototype() != null) { return getImplicitPrototype().getJSDocInfo(); } else { return super.getJSDocInfo(); } } /** * Sets the docInfo for this type from the given * {@link JSDocInfo}. The {@code JSDocInfo} may be {@code null}. */ public void setJSDocInfo(JSD

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS>ocInfo info) { docInfo = info; } /** * Detects a cycle in the implicit prototype chain. This method accesses * the {@link #getImplicitPrototype()} method and must therefore be * invoked only after the object is sufficiently initialized to respond to * calls to this method.<p> * * The method is not thread safe.<p> * * @return True iff an implicit prototype cycle was detected. */ final boolean detectImplicitPrototypeCycle() { // detecting cycle this.visited = true; ObjectType p = getImplicitPrototype(); while (p != null) { if (p.visited) { return true; } else { p.visited = true; } p = p.getImplicitPrototype(); } // clean up p = this; do { p.visited = false; p = p.getImplicitPrototype(); } while (p != null); return false; } /** * Gets the reference name for this object. This includes named types * like constructors, prototypes, and enums. It notably does not include * literal types like strings and booleans and structural types. * @return the object's name or {@code null} if this is an anonymous * object */ public abstract String getReferenceName(); /** * Due to the complexity of some of our internal type systems, sometimes * we have different types constructed by the same constructor. * In other parts of the type system, these are called delegates. * We construct these types by appending suffixes to the constructor name. * * The normalized reference name does not have these suffixes, and as such, * recollapses these implicit types back to their real type. */ public String getNormalizedReferenceName() { String name = getReferenceName(); if (name != null) { int pos = name.indexOf("("); if (pos != -1) { return name.substring(0, pos); } } return name; } @Override public String getDisplayName() { return getNormalizedReferenceName(); } /** * Creates a suffix for a proxy delegate. * @see #getNormalizedReferenceName */ public static String createDelegateSuffix(String suffix) { return "("

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS> definition of the specified property. * This could be the node corresponding to declaration of the property or the * node corresponding to the first reference to this property, e.g., * "this.propertyName" in a constructor. Note this is mainly intended to be * an estimate of where in the source code a property is defined. Sometime * the returned node is not even part of the global AST but in the AST of the * JsDoc that defines a type. * * @param propertyName the name of the property * @return the {@code Node} corresponding to the property or null. */ public Node getPropertyNode(String propertyName) { return null; } /** * Gets the docInfo on the specified property on this type. This should not * be done implemented recursively, as you generally need to know exactly on * which type in the prototype chain the JSDocInfo exists. */ public JSDocInfo getOwnPropertyJSDocInfo(String propertyName) { return null; } /** * Sets the docInfo for the specified property from the * {@link JSDocInfo} on its definition. * @param info {@code JSDocInfo} for the property definition. May be * {@code null}. * @param inExterns {@code true} if this property was defined in an externs * file. TightenTypes assumes that any function passed to an externs * property could be called, so setting this incorrectly could result * in live code being removed. */ public void setPropertyJSDocInfo(String propertyName, JSDocInfo info, boolean inExterns) { // by default, do nothing } @Override public JSType findPropertyType(String propertyName) { return hasProperty(propertyName) ? getPropertyType(propertyName) : null; } /** * Gets the property type of the property whose name is given. If the * underlying object does not have this property, the Unknown type is * returned to indicate that no information is available on this property. * * @return the property's type or {@link UnknownType}. This method never * returns {@code null}. */ public abstract JSType getPropertyType(String propertyName); /** * Checks whether the property whose name is given is present on the

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS>Name, int lineno, int charno) { super(registry, registry.getNativeObjectType(JSTypeNative.UNKNOWN_TYPE)); Preconditions.checkNotNull(reference); this.reference = reference; this.sourceName = sourceName; this.lineno = lineno; this.charno = charno; } @Override boolean defineProperty(String propertyName, JSType type, boolean inferred, boolean inExterns, Node propertyNode) { if (!isResolved()) { // If this is an unresolved object type, we need to save all its // properties and define them when it is resolved. if (propertyContinuations == null) { propertyContinuations = Lists.newArrayList(); } propertyContinuations.add( new PropertyContinuation( propertyName, type, inferred, inExterns, propertyNode)); return true; } else { return super.defineProperty( propertyName, type, inferred, inExterns, propertyNode); } } private void finishPropertyContinuations() { ObjectType referencedObjType = getReferencedObjTypeInternal(); if (referencedObjType != null && !referencedObjType.isUnknownType()) { if (propertyContinuations != null) { for (PropertyContinuation c : propertyContinuations) { c.commit(this); } } } propertyContinuations = null; } /** Returns the type to which this refers (which is unknown if unresolved). */ public JSType getReferencedType() { return getReferencedTypeInternal(); } @Override public String getReferenceName() { return reference; } @Override public String toString() { return reference; } @Override public boolean hasReferenceName() { return true; } @Override boolean isNamedType() { return true; } @Override public boolean isNominalType() { return true; } /** * Two named types are equivalent if they are the same {@code * ObjectType} object. This is complicated by the fact that isEquivalent * is sometimes called before we have a chance to resolve the type * names. * * @return {@code true} iff {@code that} == {@code this} or {@code that} * is a {@link NamedType} whose reference is the same as ours,

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS><JSType> enclosing) { JSType value = lookupViaProperties(t, enclosing); // last component of the chain if ((value instanceof FunctionType) && (value.isConstructor() || value.isInterface())) { FunctionType functionType = (FunctionType) value; setReferencedAndResolvedType( functionType.getInstanceType(), t, enclosing); } else if (value instanceof EnumType) { setReferencedAndResolvedType( ((EnumType) value).getElementsType(), t, enclosing); } else { // We've been running into issues where people forward-declare // non-named types. (This is legitimate...our dependency management // code doubles as our forward-declaration code.) // // So if the type does resolve to an actual value, but it's not named, // then don't respect the forward declaration. handleUnresolvedType(t, value == null || value.isUnknownType()); } } /** * Resolves a type by looking up its first component in the scope, and * subsequent components as properties. The scope must have been fully * parsed and a symbol table constructed. * @return The type of the symbol, or null if the type could not be found. */ private JSType lookupViaProperties( ErrorReporter t, StaticScope<JSType> enclosing) { String[] componentNames = reference.split("\\.", -1); if (componentNames[0].length() == 0) { return null; } StaticSlot<JSType> slot = enclosing.getSlot(componentNames[0]); if (slot == null) { return null; } // If the first component has a type of 'Unknown', then any type // names using it should be regarded as silently 'Unknown' rather than be // noisy about it. JSType slotType = slot.getType(); if (slotType == null || slotType.isAllType() || slotType.isNoType()) { return null; } JSType value = getTypedefType(t, slot, componentNames[0]); if (value == null) { return null; } // resolving component by component for (int i = 1; i < componentNames.length; i++) { ObjectType parent

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS>Class = ObjectType.cast(value); if (parentClass == null) { return null; } if (componentNames[i].length() == 0) { return null; } value = parentClass.getPropertyType(componentNames[i]); } return value; } private void setReferencedAndResolvedType(JSType type, ErrorReporter t, StaticScope<JSType> enclosing) { if (validator != null) { validator.apply(type); } setReferencedType(type); checkEnumElementCycle(t); setResolvedTypeInternal(getReferencedType()); } private void handleTypeCycle(ErrorReporter t) { setReferencedType( registry.getNativeObjectType(JSTypeNative.UNKNOWN_TYPE)); t.warning("Cycle detected in inheritance chain of type " + reference, sourceName, lineno, null, charno); setResolvedTypeInternal(getReferencedType()); } private void checkEnumElementCycle(ErrorReporter t) { JSType referencedType = getReferencedType(); if (referencedType instanceof EnumElementType && ((EnumElementType) referencedType).getPrimitiveType() == this) { handleTypeCycle(t); } } // Warns about this type being unresolved iff it's not a forward-declared // type name. private void handleUnresolvedType( ErrorReporter t, boolean ignoreForwardReferencedTypes) { if (registry.isLastGeneration()) { boolean isForwardDeclared = ignoreForwardReferencedTypes && registry.isForwardDeclaredType(reference); if (!isForwardDeclared && registry.isLastGeneration()) { t.warning("Bad type annotation. Unknown type " + reference, sourceName, lineno, null, charno); } else { setReferencedType( registry.getNativeObjectType( JSTypeNative.NO_RESOLVED_TYPE)); if (registry.isLastGeneration() && validator != null) { validator.apply(getReferencedType()); } } setResolvedTypeInternal(getReferencedType()); } else { setResolvedTypeInternal(this); } } JSType getTypedefType(ErrorReporter t, StaticSlot<JSType> slot, String name) { JSType type = slot.getType(); if (type != null) { return type;

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS> This is invalid, but allow it so the checks can catch it. return; } declareVar(n.getFirstChild()); return; // should not examine function's children case Token.CATCH: Preconditions.checkState(n.getChildCount() == 2); Preconditions.checkState(n.getFirstChild().getType() == Token.NAME); // the first child is the catch var and the third child // is the code block final Node var = n.getFirstChild(); final Node block = var.getNext(); declareVar(var); scanVars(block, n); return; // only one child to scan case Token.SCRIPT: sourceName = (String) n.getProp(Node.SOURCENAME_PROP); break; } // Variables can only occur in statement-level nodes, so // we only need to traverse children in a couple special cases. if (NodeUtil.isControlStructure(n) || NodeUtil.isStatementBlock(n)) { for (Node child = n.getFirstChild(); child != null;) { Node next = child.getNext(); scanVars(child, n); child = next; } } } /** * Interface for injectable duplicate handling. */ interface RedeclarationHandler { void onRedeclaration( Scope s, String name, Node n, CompilerInput input); } /** * The default handler for duplicate declarations. */ private class DefaultRedeclarationHandler implements RedeclarationHandler { public void onRedeclaration( Scope s, String name, Node n, CompilerInput input) { Node parent = n.getParent(); // Don't allow multiple variables to be declared at the top level scope if (scope.isGlobal()) { Scope.Var origVar = scope.getVar(name); Node origParent = origVar.getParentNode(); if (origParent.getType() == Token.CATCH && parent.getType() == Token.CATCH) { // Okay, both are 'catch(x)' variables. return; } boolean allowDupe = false; JSDocInfo info = n.getJSDocInfo(); if (info == null) { info = parent.getJSDocInfo(); } allowDupe = info != null && info.get

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS>Name)) { return lhsOfDotName + delimiter; } else { return lhsOfDotName + delimiter + rhsOfDotName; } case Token.GETELEM: Node outsideBrackets = node.getFirstChild(); Node insideBrackets = outsideBrackets.getNext(); String nameOutsideBrackets = getName(outsideBrackets); String nameInsideBrackets = getName(insideBrackets); if ("prototype".equals(nameInsideBrackets)) { return nameOutsideBrackets + delimiter; } else { return nameOutsideBrackets + delimiter + nameInsideBrackets; } case Token.NAME: return node.getString(); case Token.STRING: return TokenStream.isJSIdentifier(node.getString()) ? node.getString() : ("__" + nextUniqueInt++); case Token.NUMBER: return NodeUtil.getStringValue(node); case Token.THIS: return "this"; case Token.CALL: return getName(node.getFirstChild()); default: StringBuilder sb = new StringBuilder(); for (Node child = node.getFirstChild(); child != null; child = child.getNext()) { if (sb.length() > 0) { sb.append(delimiter); } sb.append(getName(child)); } return sb.toString(); } } }

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS> assign {@code x} a type within the {@code f(x)} * call. Since it has no possible type, we assign {@code x} the NoType, * so that {@code f(x)} is legal no matter what the type of {@code f}'s * first argument is. * * @see <a href="http://en.wikipedia.org/wiki/Bottom_type">Bottom types</a> */ public class NoType extends NoObjectType { private static final long serialVersionUID = 1L; NoType(JSTypeRegistry registry) { super(registry); } @Override public boolean isNoObjectType() { return false; } @Override public boolean isNoType() { return true; } @Override public boolean isNullable() { return true; } @Override public boolean isSubtype(JSType that) { return true; } @Override public BooleanLiteralSet getPossibleToBooleanOutcomes() { return BooleanLiteralSet.EMPTY; } @Override public boolean matchesNumberContext() { return true; } @Override public boolean matchesObjectContext() { return true; } @Override public boolean matchesStringContext() { return true; } @Override public <T> T visit(Visitor<T> visitor) { return visitor.caseNoType(); } @Override public String toString() { return "None"; } }

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS>type); // If this is a prototype property, then we want to skip assignments // to the instance type as well. These assignments are not usually // seen in the extern code itself, so we must handle them here. if ((type = typeSystem.getInstanceFromPrototype(type)) != null) { prop.getTypes().add(type); prop.typesToSkip.add(type); } } } } } /** * Traverses the tree, building a map from field names to Nodes for all * fields that can be renamed. */ private class FindRenameableProperties extends AbstractScopingCallback { @Override public void visit(NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.GETPROP) { handleGetProp(t, n); } else if (n.getType() == Token.OBJECTLIT) { handleObjectLit(t, n); } } /** * Processes a GETPROP node. */ private void handleGetProp(NodeTraversal t, Node n) { String name = n.getLastChild().getString(); T type = typeSystem.getType(getScope(), n.getFirstChild(), name); Property prop = getProperty(name); if (!prop.scheduleRenaming(n.getLastChild(), processProperty(t, prop, type, null))) { if (showInvalidationWarnings) { compiler.report(JSError.make( t.getSourceName(), n, Warnings.INVALIDATION, name, (type == null ? "null" : type.toString()), n.toString())); } } } /** * Processes a OBJECTLIT node. */ private void handleObjectLit(NodeTraversal t, Node n) { Node child = n.getFirstChild(); while (child != null) { // Maybe STRING, NUMBER, GET, SET if (child.getType() != Token.NUMBER) { // We should never see a mix of numbers and strings. String name = child.getString(); T type = typeSystem.getType(getScope(), n, name); Property prop = getProperty(name); if (!prop.scheduleRenaming(child, processProperty(t, prop, type, null))) { if (showInvalidationWarnings)

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS> { compiler.report(JSError.make( t.getSourceName(), child, Warnings.INVALIDATION, name, (type == null ? "null" : type.toString()), n.toString())); } } } child = child.getNext(); } } /** * Processes a property, adding it to the list of properties to rename. * @return a representative type for the property reference, which will be * the highest type on the prototype chain of the provided type. In the * case of a union type, it will be the highest type on the prototype * chain of one of the members of the union. */ private T processProperty( NodeTraversal t, Property prop, T type, T relatedType) { type = typeSystem.restrictByNotNullOrUndefined(type); if (prop.skipRenaming || typeSystem.isInvalidatingType(type)) { return null; } Iterable<T> alternatives = typeSystem.getTypeAlternatives(type); if (alternatives != null) { T firstType = relatedType; for (T subType : alternatives) { T lastType = processProperty(t, prop, subType, firstType); if (lastType != null) { firstType = firstType == null ? lastType : firstType; } } return firstType; } else { T topType = typeSystem.getTypeWithProperty(prop.name, type); if (typeSystem.isInvalidatingType(topType)) { return null; } prop.addType(type, topType, relatedType); return topType; } } } /** Renames all properties with references on more than one type. */ void renameProperties() { int propsRenamed = 0, propsSkipped = 0, instancesRenamed = 0, instancesSkipped = 0, singleTypeProps = 0; for (Property prop : properties.values()) { if (prop.shouldRename()) { Map<T, String> propNames = buildPropNames(prop.getTypes(), prop.name); ++propsRenamed; prop.expandTypesToSkip(); UnionFind<T> types = prop.getTypes(); for (Node node : prop.renameNodes)

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS> { T rootType = prop.rootTypes.get(node); if (prop.shouldRename(rootType)) { String newName = propNames.get(rootType); node.setString(newName); compiler.reportCodeChange(); ++instancesRenamed; } else { ++instancesSkipped; } } } else { if (prop.skipRenaming) { ++propsSkipped; } else { ++singleTypeProps; } } } logger.info("Renamed " + instancesRenamed + " instances of " + propsRenamed + " properties."); logger.info("Skipped renaming " + instancesSkipped + " invalidated " + "properties, " + propsSkipped + " instances of properties " + "that were skipped for specific types and " + singleTypeProps + " properties that were referenced from only one type."); } /** * Chooses a name to use for renaming in each equivalence class and maps * each type in that class to it. */ private Map<T, String> buildPropNames(UnionFind<T> types, String name) { Map<T, String> names = Maps.newHashMap(); for (Set<T> set : types.allEquivalenceClasses()) { checkState(!set.isEmpty()); String typeName = null; for (T type : set) { if (typeName == null || type.toString().compareTo(typeName) < 0) { typeName = type.toString(); } } String newName; if ("{...}".equals(typeName)) { newName = name; } else { newName = typeName.replaceAll("[^\\w$]", "_") + "$" + name; } for (T type : set) { names.put(type, newName); } } return names; } /** Returns a map from field name to types for which it will be renamed. */ Multimap<String, Collection<T>> getRenamedTypesForTesting() { Multimap<String, Collection<T>> ret = HashMultimap.create(); for (Map.Entry<String, Property> entry: properties.entrySet()) { Property prop = entry.getValue(); if (!prop.skipRenaming) { for (Collection<T> c : prop.getTypes

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS> node; } @Override public List<GraphNode<N, E>> getNeighborNodes(N value) { UndiGraphNode<N, E> uNode = getUndirectedGraphNode(value); List<GraphNode<N, E>> nodeList = Lists.newArrayList(); for (Iterator<GraphNode<N, E>> i = getNeighborNodesIterator(value); i.hasNext();) { nodeList.add(i.next()); } return nodeList; } @Override public Iterator<GraphNode<N, E>> getNeighborNodesIterator(N value) { UndiGraphNode<N, E> uNode = getUndirectedGraphNode(value); Preconditions.checkNotNull(uNode, value + " should be in the graph."); return ((LinkedUndirectedGraphNode<N, E>) uNode).neighborIterator(); } @SuppressWarnings("unchecked") @Override public List<UndiGraphEdge<N, E>> getUndirectedGraphEdges(N n1, N n2) { UndiGraphNode<N, E> dNode1 = nodes.get(n1); if (dNode1 == null) { return null; } UndiGraphNode<N, E> dNode2 = nodes.get(n2); if (dNode2 == null) { return null; } List<UndiGraphEdge<N, E>> edges = Lists.newArrayList(); for (UndiGraphEdge<N, E> outEdge : dNode1.getNeighborEdges()) { if (outEdge.getNodeA() == dNode2 || outEdge.getNodeB() == dNode2) { edges.add(outEdge); } } return edges; } @Override public UndiGraphNode<N, E> getUndirectedGraphNode(N nodeValue) { return nodes.get(nodeValue); } @Override public Collection<UndiGraphNode<N, E>> getUndirectedGraphNodes() { return Collections.<UndiGraphNode<N, E>>unmodifiableCollection( nodes.values()); } @Override public GraphNode<N, E> createNode(N value) { return createUndirectedGraphNode(value); } @Override public List

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS><GraphEdge<N, E>> getEdges(N n1, N n2) { return Collections.<GraphEdge<N, E>>unmodifiableList( getUndirectedGraphEdges(n1, n2)); } @Override public GraphEdge<N, E> getFirstEdge(N n1, N n2) { UndiGraphNode<N, E> dNode1 = getNodeOrFail(n1); UndiGraphNode<N, E> dNode2 = getNodeOrFail(n2); for (UndiGraphEdge<N, E> outEdge : dNode1.getNeighborEdges()) { if (outEdge.getNodeA() == dNode2 || outEdge.getNodeB() == dNode2) { return outEdge; } } return null; } @Override public GraphNode<N, E> getNode(N value) { return getUndirectedGraphNode(value); } @Override public boolean isConnected(N n1, N n2) { return isConnected(n1, Predicates.<E>alwaysTrue(), n2); } @Override public boolean isConnected(N n1, E e, N n2) { return isConnected(n1, Predicates.<E>equalTo(e), n2); } private boolean isConnected(N n1, Predicate<E> edgePredicate, N n2) { UndiGraphNode<N, E> dNode1 = nodes.get(n1); if (dNode1 == null) { return false; } UndiGraphNode<N, E> dNode2 = nodes.get(n2); if (dNode2 == null) { return false; } for (UndiGraphEdge<N, E> outEdge : dNode1.getNeighborEdges()) { if ((outEdge.getNodeA() == dNode1 && outEdge.getNodeB() == dNode2) || (outEdge.getNodeA() == dNode2 && outEdge.getNodeB() == dNode1)) { if (edgePredicate.apply(outEdge.getValue())) { return true; } } } return false; } @Override public List<GraphvizEdge> getGraphvizEdges() {

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS> List<GraphvizEdge> edgeList = Lists.newArrayList(); for (LinkedUndirectedGraphNode<N, E> node : nodes.values()) { for (UndiGraphEdge<N, E> edge : node.getNeighborEdges()) { if (edge.getNodeA() == node) { edgeList.add((GraphvizEdge) edge); } } } return edgeList; } @Override public String getName() { return "LinkedUndirectedGraph"; } @Override public List<GraphvizNode> getGraphvizNodes() { List<GraphvizNode> nodeList = Lists.newArrayListWithCapacity(nodes.size()); for (LinkedUndirectedGraphNode<N, E> node : nodes.values()) { nodeList.add(node); } return nodeList; } @Override public boolean isDirected() { return false; } @Override public Collection<GraphNode<N, E>> getNodes() { return Collections.<GraphNode<N, E>> unmodifiableCollection(nodes.values()); } @SuppressWarnings("unchecked") @Override public List<GraphEdge<N, E>> getEdges() { List<GraphEdge<N, E>> result = Lists.newArrayList(); for (LinkedUndirectedGraphNode<N, E> node : nodes.values()) { for (UndiGraphEdge<N, E> edge : node.getNeighborEdges()) { if (edge.getNodeA() == node) { result.add(edge); } } } return result; } @Override public int getNodeDegree(N value) { UndiGraphNode<N, E> uNode = getUndirectedGraphNode(value); if (uNode == null) { throw new IllegalArgumentException(value + " not found in graph"); } return uNode.getNeighborEdges().size(); } /** * An undirected graph node that stores outgoing edges and incoming edges as * an list within the node itself. */ static class LinkedUndirectedGraphNode<N, E> implements UndiGraphNode<N, E>, GraphvizNode { private List<UndiGraphEdge<N, E>> neighborList = Lists.newArrayList(); private final N value;

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS> LinkedUndirectedGraphNode(N nodeValue) { this.value = nodeValue; } @Override public List<UndiGraphEdge<N, E>> getNeighborEdges() { return neighborList; } public Iterator<UndiGraphEdge<N, E>> getNeighborEdgesIterator() { return neighborList.iterator(); } @Override public <A extends Annotation> A getAnnotation() { throw new UnsupportedOperationException( "Graph initialized with node annotations turned off"); } @Override public void setAnnotation(Annotation data) { throw new UnsupportedOperationException( "Graph initialized with node annotations turned off"); } @Override public N getValue() { return value; } @Override public String getColor() { return "white"; } @Override public String getId() { return "LDN" + hashCode(); } @Override public String getLabel() { return value != null ? value.toString() : "null"; } public Iterator<GraphNode<N, E>> neighborIterator() { return new NeighborIterator(); } private class NeighborIterator implements Iterator<GraphNode<N, E>> { private final Iterator<UndiGraphEdge<N, E>> edgeIterator = neighborList.iterator(); @Override public boolean hasNext() { return edgeIterator.hasNext(); } @Override public GraphNode<N, E> next() { UndiGraphEdge<N, E> edge = edgeIterator.next(); if (edge.getNodeA() == LinkedUndirectedGraphNode.this) { return edge.getNodeB(); } else { return edge.getNodeA(); } } @Override public void remove() { throw new UnsupportedOperationException("Remove not supported."); } } } /** * An undirected graph node with annotations. */ static class AnnotatedLinkedUndirectedGraphNode<N, E> extends LinkedUndirectedGraphNode<N, E> { protected Annotation annotation; AnnotatedLinkedUndirectedGraphNode(N nodeValue) { super(nodeValue); } @SuppressWarnings("unchecked") @Override public <A extends Annotation> A getAnnotation() { return (A) annotation; } @Override public void setAnnotation(Annotation data)

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS> { annotation = data; } } /** * An undirected graph edge that stores two nodes at each edge. */ static class LinkedUndirectedGraphEdge<N, E> implements UndiGraphEdge<N, E>, GraphvizEdge { private UndiGraphNode<N, E> nodeA; private UndiGraphNode<N, E> nodeB; protected final E value; LinkedUndirectedGraphEdge(UndiGraphNode<N, E> nodeA, E edgeValue, UndiGraphNode<N, E> nodeB) { this.value = edgeValue; this.nodeA = nodeA; this.nodeB = nodeB; } @Override public E getValue() { return value; } @Override public GraphNode<N, E> getNodeA() { return nodeA; } @Override public GraphNode<N, E> getNodeB() { return nodeB; } @Override public <A extends Annotation> A getAnnotation() { throw new UnsupportedOperationException( "Graph initialized with edge annotations turned off"); } @Override public void setAnnotation(Annotation data) { throw new UnsupportedOperationException( "Graph initialized with edge annotations turned off"); } @Override public String getColor() { return "black"; } @Override public String getLabel() { return value != null ? value.toString() : "null"; } @SuppressWarnings("unchecked") @Override public String getNode1Id() { return ((LinkedUndirectedGraphNode<N, E>) nodeA).getId(); } @SuppressWarnings("unchecked") @Override public String getNode2Id() { return ((LinkedUndirectedGraphNode<N, E>) nodeB).getId(); } @Override public String toString() { return nodeA.toString() + " -- " + nodeB.toString(); } } /** * An annotated undirected graph edge.. */ static class AnnotatedLinkedUndirectedGraphEdge<N, E> extends LinkedUndirectedGraphEdge<N, E> { protected Annotation annotation; AnnotatedLinkedUndirectedGraphEdge( UndiGraphNode<N, E> nodeA, E edgeValue, UndiGraphNode<N, E

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS> if (superConstructor != null) { ObjectType superInstance = funType.getSuperClassConstructor().getInstanceType(); if (!superInstance.toString().equals("Object")) { sb.append(" * @extends {" + superInstance + "}\n"); } } // Avoid duplicates, add implemented type to a set first Set<String> interfaces = Sets.newTreeSet(); for (ObjectType interfaze : funType.getImplementedInterfaces()) { interfaces.add(interfaze.toString()); } for (String interfaze : interfaces) { sb.append(" * @implements {" + interfaze + "}\n"); } if (funType.isConstructor()) { sb.append(" * @constructor\n"); } else if (funType.isInterface()) { sb.append(" * @interface\n"); } } if (fnNode != null && fnNode.getBooleanProp(Node.IS_DISPATCHER)) { sb.append(" * @javadispatch\n"); } sb.append(" */\n"); return sb.toString(); } /** * Creates a JSDoc-suitable String representation the type of a parameter. * * @param parameterNode The parameter node. */ private String getParameterNodeJSDocType(Node parameterNode) { JSType parameterType = parameterNode.getJSType(); String typeString; // Emit unknown types as '*' (AllType) since '?' (UnknownType) is not // a valid JSDoc type. if (parameterType.isUnknownType()) { typeString = "*"; } else { // Fix-up optional and vararg parameters to match JSDoc type language if (parameterNode.isOptionalArg()) { typeString = parameterType.restrictByNotNullOrUndefined() + "="; } else if (parameterNode.isVarArgs()) { typeString = "..." + parameterType.restrictByNotNullOrUndefined(); } else { typeString = parameterType.toString(); } } return typeString; } }

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS> (NodeUtil.isFunctionDeclaration(n)) { Node nameNode = n.getFirstChild(); renamer.addDeclaredName(nameNode.getString()); } for (Node c = n.getFirstChild(); c != null; c = c.getNext()) { findDeclaredNames(c, n, renamer); } } } /** * Declared names renaming policy interface. */ interface Renamer { /** * Called when a declared name is found in the local current scope. */ void addDeclaredName(String name); /** * @return A replacement name, null if oldName is unknown or should not * be replaced. */ String getReplacementName(String oldName); /** * @return Whether the constant-ness of a name should be removed. */ boolean stripConstIfReplaced(); /** * @return A Renamer for a scope within the scope of the current Renamer. */ Renamer forChildScope(); } /** * Inverts the transformation by {@link ContextualRenamer}, when possible. */ static class ContextualRenameInverter implements ScopedCallback, CompilerPass { private final AbstractCompiler compiler; // The set of names referenced in the current scope. private Set<String> referencedNames = ImmutableSet.of(); // Stack reference sets. private Deque<Set<String>> referenceStack = new ArrayDeque<Set<String>>(); // Name are globally unique initially, so we don't need a per-scope map. private Map<String, List<Node>> nameMap = Maps.newHashMap(); private ContextualRenameInverter(AbstractCompiler compiler) { this.compiler = compiler; } public void process(Node externs, Node js) { NodeTraversal.traverse(compiler, js, this); } public static String getOrginalName(String name) { int index = indexOfSeparator(name); return (index == -1) ? name : name.substring(0, index); } private static int indexOfSeparator(String name) { return name.lastIndexOf(ContextualRenamer.UNIQUE_ID_SEPARATOR); } private boolean containsSeparator(String name) { return name.indexOf(ContextualRenamer.UNIQUE_ID_SEPARATOR) != -1; } /**

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS>Property>(FREQUENCY_COMPARATOR); propsByFreq.addAll(propertyMap.values()); generateNames(propsByFreq, reservedNames); // Update the string nodes. boolean changed = false; for (Node n : stringNodesToRename) { String oldName = n.getString(); Property p = propertyMap.get(oldName); if (p != null && p.newName != null) { Preconditions.checkState(oldName.equals(p.oldName)); n.setString(p.newName); changed = changed || !p.newName.equals(oldName); } } // Update the call nodes. for (Node n : callNodeToParentMap.keySet()) { Node parent = callNodeToParentMap.get(n); Node firstArg = n.getFirstChild().getNext(); StringBuilder sb = new StringBuilder(); for (String oldName : firstArg.getString().split("[.]")) { Property p = propertyMap.get(oldName); String replacement; if (p != null && p.newName != null) { Preconditions.checkState(oldName.equals(p.oldName)); replacement = p.newName; } else { replacement = oldName; } if (sb.length() > 0) { sb.append('.'); } sb.append(replacement); } parent.replaceChild(n, Node.newString(sb.toString())); changed = true; } if (changed) { compiler.reportCodeChange(); } compiler.setLifeCycleStage(LifeCycleStage.NORMALIZED_OBFUSCATED); } /** * Runs through the list of properties and renames as many as possible with * names from the previous compilation. Also, updates reservedNames with the * set of reused names. * @param reservedNames Reserved names to use during renaming. * @param allProps Properties to rename. */ private void reusePropertyNames(Set<String> reservedNames, Collection<Property> allProps) { for (Property prop : allProps) { // Check if this node can reuse a name from a previous compilation - if // it can set the newName for the property too. String prevName = prevUsedPropertyMap.lookupNewName(prop

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS> } @Override public boolean isConstructor() { return referencedType.isConstructor(); } @Override public boolean isNominalType() { return referencedType.isNominalType(); } @Override public boolean isInstanceType() { return referencedType.isInstanceType(); } @Override public boolean isInterface() { return referencedType.isInterface(); } @Override public boolean isOrdinaryFunction() { return referencedType.isOrdinaryFunction(); } @Override public TernaryValue testForEquality(JSType that) { return referencedType.testForEquality(that); } @Override public boolean isSubtype(JSType that) { return referencedType.isSubtype(that); } @Override public Iterable<ObjectType> getCtorImplementedInterfaces() { return referencedObjType == null ? Collections.<ObjectType>emptyList() : referencedObjType.getCtorImplementedInterfaces(); } @Override public boolean canAssignTo(JSType that) { return referencedType.canAssignTo(that); } @Override public boolean isEquivalentTo(JSType that) { if (this == that) { return true; } return referencedType.isEquivalentTo(that); } @Override public int hashCode() { return referencedType.hashCode(); } @Override public String toString() { return referencedType.toString(); } @Override public ObjectType getImplicitPrototype() { return referencedObjType == null ? null : referencedObjType.getImplicitPrototype(); } @Override boolean defineProperty(String propertyName, JSType type, boolean inferred, boolean inExterns, Node propertyNode) { return referencedObjType == null ? true : referencedObjType.defineProperty( propertyName, type, inferred, inExterns, propertyNode); } @Override public boolean isPropertyTypeDeclared(String propertyName) { return referencedObjType == null ? false : referencedObjType.isPropertyTypeDeclared(propertyName); } @Override public Node getPropertyNode(String propertyName) { return referencedObjType == null ? null : referencedObjType.getPropertyNode(propertyName); } @Override public boolean isPropertyTypeInferred(String propertyName) { return referencedObjType == null ? false : referencedObjType.isPropertyTypeInferred(propertyName); }

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS> @Override public boolean isPropertyInExterns(String propertyName) { return referencedObjType == null ? false : referencedObjType.isPropertyInExterns(propertyName); } @Override public int getPropertiesCount() { return referencedObjType == null ? 0 : referencedObjType.getPropertiesCount(); } @Override protected void collectPropertyNames(Set<String> props) { if (referencedObjType != null) { referencedObjType.collectPropertyNames(props); } } @Override public JSType findPropertyType(String propertyName) { return referencedType.findPropertyType(propertyName); } @Override public JSType getPropertyType(String propertyName) { return referencedObjType == null ? getNativeType(JSTypeNative.UNKNOWN_TYPE) : referencedObjType.getPropertyType(propertyName); } @Override public JSDocInfo getJSDocInfo() { return referencedType.getJSDocInfo(); } @Override public void setJSDocInfo(JSDocInfo info) { if (referencedObjType != null) { referencedObjType.setJSDocInfo(info); } } @Override public JSDocInfo getOwnPropertyJSDocInfo(String propertyName) { return referencedObjType == null ? null : referencedObjType.getOwnPropertyJSDocInfo(propertyName); } @Override public void setPropertyJSDocInfo(String propertyName, JSDocInfo info, boolean inExterns) { if (referencedObjType != null) { referencedObjType.setPropertyJSDocInfo(propertyName, info, inExterns); } } @Override public boolean hasProperty(String propertyName) { return referencedObjType == null ? false : referencedObjType.hasProperty(propertyName); } @Override public boolean hasOwnProperty(String propertyName) { return referencedObjType == null ? false : referencedObjType.hasOwnProperty(propertyName); } @Override public Set<String> getOwnPropertyNames() { return referencedObjType == null ? ImmutableSet.<String>of() : referencedObjType.getOwnPropertyNames(); } @Override public FunctionType getConstructor() { return referencedObjType == null ? null : referencedObjType.getConstructor(); } @Override public JSType getParameterType() { return referencedObjType == null

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS> shouldEmitDeprecationWarning(t, n, parent)) { if (!deprecationInfo.isEmpty()) { compiler.report( t.makeError(n, DEPRECATED_CLASS_REASON, type.toString(), deprecationInfo)); } else { compiler.report( t.makeError(n, DEPRECATED_CLASS, type.toString())); } } } } /** * Checks the given NAME node to ensure that access restrictions are obeyed. */ private void checkNameDeprecation(NodeTraversal t, Node n, Node parent) { // Don't bother checking definitions or constructors. if (parent.getType() == Token.FUNCTION || parent.getType() == Token.VAR || parent.getType() == Token.NEW) { return; } Scope.Var var = t.getScope().getVar(n.getString()); JSDocInfo docInfo = var == null ? null : var.getJSDocInfo(); if (docInfo != null && docInfo.isDeprecated() && shouldEmitDeprecationWarning(t, n, parent)) { if (docInfo.getDeprecationReason() != null) { compiler.report( t.makeError(n, DEPRECATED_NAME_REASON, n.getString(), docInfo.getDeprecationReason())); } else { compiler.report( t.makeError(n, DEPRECATED_NAME, n.getString())); } } } /** * Checks the given GETPROP node to ensure that access restrictions are * obeyed. */ private void checkPropertyDeprecation(NodeTraversal t, Node n, Node parent) { // Don't bother checking constructors. if (parent.getType() == Token.NEW) { return; } ObjectType objectType = ObjectType.cast(dereference(n.getFirstChild().getJSType())); String propertyName = n.getLastChild().getString(); if (objectType != null) { String deprecationInfo = getPropertyDeprecationInfo(objectType, propertyName); if (deprecationInfo != null && shouldEmitDeprecationWarning(t, n, parent)) { if (!deprecationInfo.isEmpty()) { compiler.report( t.makeError(n, DEPRECATED_PROP_REASON, propertyName, validator.getReadableJSTypeName(n

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS>.getFirstChild(), true), deprecationInfo)); } else { compiler.report( t.makeError(n, DEPRECATED_PROP, propertyName, validator.getReadableJSTypeName(n.getFirstChild(), true))); } } } } /** * Determines whether the given name is visible in the current context. * @param t The current traversal. * @param name The name node. */ private void checkNameVisibility(NodeTraversal t, Node name, Node parent) { Var var = t.getScope().getVar(name.getString()); if (var != null) { JSDocInfo docInfo = var.getJSDocInfo(); if (docInfo != null) { // If a name is private, make sure that we're in the same file. Visibility visibility = docInfo.getVisibility(); if (visibility == Visibility.PRIVATE && !t.getInput().getName().equals(docInfo.getSourceName())) { if (docInfo.isConstructor() && isValidPrivateConstructorAccess(parent)) { return; } compiler.report( t.makeError(name, BAD_PRIVATE_GLOBAL_ACCESS, name.getString(), docInfo.getSourceName())); } } } } /** * Determines whether the given property with @const tag got reassigned * @param t The current traversal. * @param getprop The getprop node. */ private void checkConstantProperty(NodeTraversal t, Node getprop) { // Check whether the property is modified Node parent = getprop.getParent(); if (!(NodeUtil.isAssignmentOp(parent) && parent.getFirstChild() == getprop) && (parent.getType() != Token.INC) && (parent.getType() != Token.DEC)) { return; } ObjectType objectType = ObjectType.cast(dereference(getprop.getFirstChild().getJSType())); String propertyName = getprop.getLastChild().getString(); // Check whether constant properties are reassigned if (objectType != null) { ObjectType oType = objectType; while (oType != null) { if (oType.hasReferenceName()) { if (initializedConstantProperties.containsEntry( oType.getReferenceName(), propertyName)) { compiler.report( t.

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS>makeError(getprop, CONST_PROPERTY_REASSIGNED_VALUE, propertyName)); break; } } oType = oType.getImplicitPrototype(); } JSDocInfo info = objectType.getOwnPropertyJSDocInfo(propertyName); if (info != null && info.isConstant() && objectType.hasReferenceName()) { initializedConstantProperties.put(objectType.getReferenceName(), propertyName); } // Add the prototype when we're looking at an instance object if (objectType.isInstanceType()) { ObjectType prototype = objectType.getImplicitPrototype(); if (prototype != null) { JSDocInfo prototypeInfo = prototype.getOwnPropertyJSDocInfo(propertyName); if (prototypeInfo != null && prototypeInfo.isConstant() && prototype.hasReferenceName()) { initializedConstantProperties.put(prototype.getReferenceName(), propertyName); } } } } } /** * Determines whether the given property is visible in the current context. * @param t The current traversal. * @param getprop The getprop node. */ private void checkPropertyVisibility(NodeTraversal t, Node getprop, Node parent) { ObjectType objectType = ObjectType.cast(dereference(getprop.getFirstChild().getJSType())); String propertyName = getprop.getLastChild().getString(); if (objectType != null) { // Is this a normal property access, or are we trying to override // an existing property? boolean isOverride = t.inGlobalScope() && parent.getType() == Token.ASSIGN && parent.getFirstChild() == getprop; // Find the lowest property defined on a class with visibility // information. if (isOverride) { objectType = objectType.getImplicitPrototype(); } JSDocInfo docInfo = null; for (; objectType != null; objectType = objectType.getImplicitPrototype()) { docInfo = objectType.getOwnPropertyJSDocInfo(propertyName); if (docInfo != null && docInfo.getVisibility() != Visibility.INHERITED) { break; } } if (objectType == null) { // We couldn't find a visibility modifier; assume it's public. return; } boolean sameInput =

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS> t.getInput().getName().equals(docInfo.getSourceName()); Visibility visibility = docInfo.getVisibility(); JSType ownerType = normalizeClassType(objectType); if (isOverride) { // Check an ASSIGN statement that's trying to override a property // on a superclass. JSDocInfo overridingInfo = parent.getJSDocInfo(); Visibility overridingVisibility = overridingInfo == null ? Visibility.INHERITED : overridingInfo.getVisibility(); // Check that (a) the property *can* be overridden, and // (b) that the visibility of the override is the same as the // visibility of the original property. if (visibility == Visibility.PRIVATE && !sameInput) { compiler.report( t.makeError(getprop, PRIVATE_OVERRIDE, objectType.toString())); } else if (overridingVisibility != Visibility.INHERITED && overridingVisibility != visibility) { compiler.report( t.makeError(getprop, VISIBILITY_MISMATCH, visibility.name(), objectType.toString(), overridingVisibility.name())); } } else { if (sameInput) { // private access is always allowed in the same file. return; } else if (visibility == Visibility.PRIVATE && (currentClass == null || ownerType.differsFrom(currentClass))) { if (docInfo.isConstructor() && isValidPrivateConstructorAccess(parent)) { return; } // private access is not allowed outside the file from a different // enclosing class. compiler.report( t.makeError(getprop, BAD_PRIVATE_PROPERTY_ACCESS, propertyName, validator.getReadableJSTypeName( getprop.getFirstChild(), true))); } else if (visibility == Visibility.PROTECTED) { // There are 3 types of legal accesses of a protected property: // 1) Accesses in the same file // 2) Overriding the property in a subclass // 3) Accessing the property from inside a subclass // The first two have already been checked for. if (currentClass == null || !currentClass.isSubtype(ownerType)) { compiler.report( t.makeError(getprop, BAD_PROTECTED_PROPERTY_ACCESS, propertyName, validator.getReadable

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS> Node scopeRoot = t.getScopeRoot(); Node scopeRootParent = scopeRoot.getParent(); return // Case #1 (deprecatedDepth > 0) || // Case #2 (getTypeDeprecationInfo(t.getScope().getTypeOfThis()) != null) || // Case #3 (scopeRootParent != null && scopeRootParent.getType() == Token.ASSIGN && getTypeDeprecationInfo( getClassOfMethod(scopeRoot, scopeRootParent)) != null); } /** * Returns whether this is a function node annotated as deprecated. */ private static boolean isDeprecatedFunction(Node n, Node parent) { if (n.getType() == Token.FUNCTION) { JSType type = n.getJSType(); if (type != null) { return getTypeDeprecationInfo(type) != null; } } return false; } /** * Returns the deprecation reason for the type if it is marked * as being deprecated. Returns empty string if the type is deprecated * but no reason was given. Returns null if the type is not deprecated. */ private static String getTypeDeprecationInfo(JSType type) { if (type == null) { return null; } JSDocInfo info = type.getJSDocInfo(); if (info != null && info.isDeprecated()) { if (info.getDeprecationReason() != null) { return info.getDeprecationReason(); } return ""; } ObjectType objType = ObjectType.cast(type); if (objType != null) { ObjectType implicitProto = objType.getImplicitPrototype(); if (implicitProto != null) { return getTypeDeprecationInfo(implicitProto); } } return null; } /** * Returns the deprecation reason for the property if it is marked * as being deprecated. Returns empty string if the property is deprecated * but no reason was given. Returns null if the property is not deprecated. */ private static String getPropertyDeprecationInfo(ObjectType type, String prop) { JSDocInfo info = type.getOwnPropertyJSDocInfo(prop); if (info != null && info.isDeprecated()) { if (info.getDeprecationReason() != null) { return info.getDeprecationReason();

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS> { maybeCutLine(); } void maybeCutLine() { } void endLine() { } void notePreferredLineBreak() { } void beginBlock() { if (statementNeedsEnded) { append(";"); maybeLineBreak(); } appendBlockStart(); endLine(); statementNeedsEnded = false; } void endBlock() { endBlock(false); } void endBlock(boolean shouldEndLine) { appendBlockEnd(); if (shouldEndLine) { endLine(); } statementNeedsEnded = false; } void listSeparator() { add(","); maybeLineBreak(); } /** * Indicates the end of a statement and a ';' may need to be added. * But we don't add it now, in case we're at the end of a block (in which * case we don't have to add the ';'). * See maybeEndStatement() */ void endStatement() { endStatement(false); } void endStatement(boolean needSemiColon) { if (needSemiColon) { append(";"); maybeLineBreak(); statementNeedsEnded = false; } else if (statementStarted) { statementNeedsEnded = true; } } /** * This is to be called when we're in a statement. If the prev statement * needs to be ended, add a ';'. */ void maybeEndStatement() { // Add a ';' if we need to. if (statementNeedsEnded) { append(";"); maybeLineBreak(); endLine(); statementNeedsEnded = false; } statementStarted = true; } void endFunction() { endFunction(false); } void endFunction(boolean statementContext) { sawFunction = true; if (statementContext) { endLine(); } } void beginCaseBody() { append(":"); } void endCaseBody() { } void add(String newcode) { maybeEndStatement(); if (newcode.length() == 0) { return; } char c = newcode.charAt(0); if ((isWordChar(c) || c == '\\') && isWordChar(getLastChar())) { // need space to separate

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS>. This is not pretty printing. // For example: "return foo;" append(" "); } append(newcode); } void appendOp(String op, boolean binOp) { append(op); } void addOp(String op, boolean binOp) { maybeEndStatement(); char first = op.charAt(0); char prev = getLastChar(); if ((first == '+' || first == '-') && prev == first) { // This is not pretty printing. This is to prevent misparsing of // things like "x + ++y" or "x++ + ++y" append(" "); } else if (Character.isLetter(first) && isWordChar(prev)) { // Make sure there is a space after e.g. instanceof , typeof append(" "); } else if (prev == '-' && first == '>') { // Make sure that we don't emit --> append(" "); } // Allow formating around the operator. appendOp(op, binOp); // Line breaking after an operator is always safe. Line breaking before an // operator on the other hand is not. We only line break after a bin op // because it looks strange. if (binOp) { maybeCutLine(); } } void addNumber(double x) { // This is not pretty printing. This is to prevent misparsing of x- -4 as // x--4 (which is a syntax error). char prev = getLastChar(); if (x < 0 && prev == '-') { add(" "); } if ((long) x == x) { long value = (long) x; long mantissa = value; int exp = 0; if (Math.abs(x) >= 100) { while (mantissa / 10 * Math.pow(10, exp + 1) == value) { mantissa /= 10; exp++; } } if (exp > 2) { add(Long.toString(mantissa) + "E" + Integer.toString(exp)); } else { add(Long.toString(value)); } } else { add(String.valueOf(x)); } } static boolean is

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS>. * * @return {@code true} if the alternate is in the union */ public boolean contains(JSType type) { for (JSType alt : alternates) { if (alt.isEquivalentTo(type)) { return true; } } return false; } /** * Returns a more restricted union type than {@code this} one, in which all * subtypes of {@code type} have been removed.<p> * * Examples: * <ul> * <li>{@code (number,string)} restricted by {@code number} is * {@code string}</li> * <li>{@code (null, EvalError, URIError)} restricted by * {@code Error} is {@code null}</li> * </ul> * * @param type the supertype of the types to remove from this union type */ public JSType getRestrictedUnion(JSType type) { UnionTypeBuilder restricted = new UnionTypeBuilder(registry); for (JSType t : alternates) { if (t.isUnknownType() || !t.isSubtype(type)) { restricted.addAlternate(t); } } return restricted.build(); } @Override public String toString() { StringBuilder result = new StringBuilder(); boolean firstAlternate = true; result.append("("); SortedSet<JSType> sorted = new TreeSet<JSType>(ALPHA); sorted.addAll(alternates); for (JSType t : sorted) { if (!firstAlternate) { result.append("|"); } result.append(t.toString()); firstAlternate = false; } result.append(")"); return result.toString(); } @Override public boolean isSubtype(JSType that) { // unknown if (that.isUnknownType()) { return true; } // all type if (that.isAllType()) { return true; } for (JSType element : alternates) { if (!element.isSubtype(that)) { return false; } } return true; } @Override public JSType getRestrictedTypeGivenToBooleanOutcome(boolean outcome) { // gather elements after restriction UnionTypeBuilder restricted = new UnionTypeBuilder(

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS>. return getNativeType(JSTypeNative.NO_TYPE); } @Override public boolean hasProperty(String propertyName) { // has all properties, since it is any object return true; } @Override boolean defineProperty(String propertyName, JSType type, boolean inferred, boolean inExterns, Node propertyNode) { // nothing, all properties are defined return true; } @Override public JSDocInfo getOwnPropertyJSDocInfo(String propertyName) { return null; } @Override public void setPropertyJSDocInfo(String propertyName, JSDocInfo info, boolean inExterns) { // Do nothing, specific properties do not have JSDocInfo. } @Override public boolean isPropertyTypeInferred(String propertyName) { return false; } @Override public <T> T visit(Visitor<T> visitor) { return visitor.caseNoObjectType(); } @Override public String toString() { return "NoObject"; } @Override public FunctionType getConstructor() { return null; } @Override JSType resolveInternal(ErrorReporter t, StaticScope<JSType> scope) { return this; } }

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS> */ public class ScriptRuntime { /** * No instances should be created. */ protected ScriptRuntime() { } // It is public so NativeRegExp can access it . public static boolean isJSLineTerminator(int c) { // Optimization for faster check for eol character: // they do not have 0xDFD0 bits set if ((c & 0xDFD0) != 0) { return false; } return c == '\n' || c == '\r' || c == 0x2028 || c == 0x2029; } // Can not use Double.NaN defined as 0.0d / 0.0 as under the Microsoft VM, // versions 2.01 and 3.0P1, that causes some uses (returns at least) of // Double.NaN to be converted to 1.0. // So we use ScriptRuntime.NaN instead of Double.NaN. public static final double NaN = Double.longBitsToDouble(0x7ff8000000000000L); // A similar problem exists for negative zero. public static final double negativeZero = Double.longBitsToDouble(0x8000000000000000L); public static final Double NaNobj = new Double(NaN); /* * Helper function for toNumber, parseInt, and TokenStream.getToken. */ @SuppressWarnings("fallthrough") static double stringToNumber(String s, int start, int radix) { char digitMax = '9'; char lowerCaseBound = 'a'; char upperCaseBound = 'A'; int len = s.length(); if (radix < 10) { digitMax = (char) ('0' + radix - 1); } if (radix > 10) { lowerCaseBound = (char) ('a' + radix - 10); upperCaseBound = (char) ('A' + radix - 10); } int end; double sum = 0.0; for (end=start; end < len; end++) { char c = s.charAt(end); int newDigit; if

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS> ('0' <= c && c <= digitMax) newDigit = c - '0'; else if ('a' <= c && c < lowerCaseBound) newDigit = c - 'a' + 10; else if ('A' <= c && c < upperCaseBound) newDigit = c - 'A' + 10; else break; sum = sum*radix + newDigit; } if (start == end) { return NaN; } if (sum >= 9007199254740992.0) { if (radix == 10) { /* If we're accumulating a decimal number and the number * is >= 2^53, then the result from the repeated multiply-add * above may be inaccurate. Call Java to get the correct * answer. */ try { return Double.valueOf(s.substring(start, end)).doubleValue(); } catch (NumberFormatException nfe) { return NaN; } } else if (radix == 2 || radix == 4 || radix == 8 || radix == 16 || radix == 32) { /* The number may also be inaccurate for one of these bases. * This happens if the addition in value*radix + digit causes * a round-down to an even least significant mantissa bit * when the first dropped bit is a one. If any of the * following digits in the number (which haven't been added * in yet) are nonzero then the correct action would have * been to round up instead of down. An example of this * occurs when reading the number 0x1000000000000081, which * rounds to 0x1000000000000000 instead of 0x1000000000000100. */ int bitShiftInChar = 1; int digit = 0; final int SKIP_LEADING_ZEROS = 0; final int FIRST_EXACT_53_BITS = 1; final int AFTER_BIT_53 = 2;

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS> final int ZEROS_AFTER_54 = 3; final int MIXED_AFTER_54 = 4; int state = SKIP_LEADING_ZEROS; int exactBitsLimit = 53; double factor = 0.0; boolean bit53 = false; // bit54 is the 54th bit (the first dropped from the mantissa) boolean bit54 = false; for (;;) { if (bitShiftInChar == 1) { if (start == end) break; digit = s.charAt(start++); if ('0' <= digit && digit <= '9') digit -= '0'; else if ('a' <= digit && digit <= 'z') digit -= 'a' - 10; else digit -= 'A' - 10; bitShiftInChar = radix; } bitShiftInChar >>= 1; boolean bit = (digit & bitShiftInChar) != 0; switch (state) { case SKIP_LEADING_ZEROS: if (bit) { --exactBitsLimit; sum = 1.0; state = FIRST_EXACT_53_BITS; } break; case FIRST_EXACT_53_BITS: sum *= 2.0; if (bit) sum += 1.0; --exactBitsLimit; if (exactBitsLimit == 0) { bit53 = bit; state = AFTER_BIT_53; } break; case AFTER_BIT_53: bit54 = bit; factor = 2.0; state = ZEROS_AFTER_54; break; case ZEROS_AFTER_54: if (bit) { state = MIXED_AFTER_54; } // fallthrough case MIXED_AFTER_54: factor *= 2; break; } } switch (state) { case SKIP_LEADING_ZEROS: sum = 0.0; break; case FIRST_EXACT_53_BITS: case AFTER_BIT_53: // do nothing break; case ZER

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS>OS_AFTER_54: // x1.1 -> x1 + 1 (round up) // x0.1 -> x0 (round down) if (bit54 & bit53) sum += 1.0; sum *= factor; break; case MIXED_AFTER_54: // x.100...1.. -> x + 1 (round up) // x.0anything -> x (round down) if (bit54) sum += 1.0; sum *= factor; break; } } /* We don't worry about inaccurate numbers for any other base. */ } return sum; } public static String escapeString(String s) { return escapeString(s, '"'); } /** * For escaping strings printed by object and array literals; not quite * the same as 'escape.' */ public static String escapeString(String s, char escapeQuote) { if (!(escapeQuote == '"' || escapeQuote == '\'')) Kit.codeBug(); StringBuffer sb = null; for(int i = 0, L = s.length(); i != L; ++i) { int c = s.charAt(i); if (' ' <= c && c <= '~' && c != escapeQuote && c != '\\') { // an ordinary print character (like C isprint()) and not " // or \ . if (sb != null) { sb.append((char)c); } continue; } if (sb == null) { sb = new StringBuffer(L + 3); sb.append(s); sb.setLength(i); } int escape = -1; switch (c) { case '\b': escape = 'b'; break; case '\f': escape = 'f'; break; case '\n': escape = 'n'; break; case '\r': escape = 'r'; break; case '\t': escape = 't'; break; case 0xb: escape = 'v'; break; // Java lacks \v. case ' ': escape = ' '; break; case '\\': escape = '\\'; break

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS>; } if (escape >= 0) { // an \escaped sort of character sb.append('\\'); sb.append((char)escape); } else if (c == escapeQuote) { sb.append('\\'); sb.append(escapeQuote); } else { int hexSize; if (c < 256) { // 2-digit hex sb.append("\\x"); hexSize = 2; } else { // Unicode. sb.append("\\u"); hexSize = 4; } // append hexadecimal form of c left-padded with 0 for (int shift = (hexSize - 1) * 4; shift >= 0; shift -= 4) { int digit = 0xf & (c >> shift); int hc = (digit < 10) ? '0' + digit : 'a' - 10 + digit; sb.append((char)hc); } } } return (sb == null) ? s : sb.toString(); } static boolean isValidIdentifierName(String s) { int L = s.length(); if (L == 0) return false; if (!Character.isJavaIdentifierStart(s.charAt(0))) return false; for (int i = 1; i != L; ++i) { if (!Character.isJavaIdentifierPart(s.charAt(i))) return false; } return !TokenStream.isKeyword(s); } /** * Convert the value to a string. * * See ECMA 9.8. */ public static String toString(Object val) { for (;;) { if (val == null) { return "null"; } if (val instanceof String) { return (String)val; } if (val instanceof Number) { // XXX should we just teach NativeNumber.stringValue() // about Numbers? return numberToString(((Number)val).doubleValue(), 10); } return val.toString(); } } public static String numberToString(double d, int base) { if (d != d) return "NaN"; if (d == Double.POSITIVE_INFINITY) return "Infinity";

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS> if (d == Double.NEGATIVE_INFINITY) return "-Infinity"; if (d == 0.0) return "0"; if ((base < 2) || (base > 36)) { throw Context.reportRuntimeError1( "msg.bad.radix", Integer.toString(base)); } if (base != 10) { return DToA.JS_dtobasestr(base, d); } else { StringBuffer result = new StringBuffer(); DToA.JS_dtostr(result, DToA.DTOSTR_STANDARD, 0, d); return result.toString(); } } /** * If str is a decimal presentation of Uint32 value, return it as long. * Othewise return -1L; */ public static long testUint32String(String str) { // The length of the decimal string representation of // UINT32_MAX_VALUE, 4294967296 final int MAX_VALUE_LENGTH = 10; int len = str.length(); if (1 <= len && len <= MAX_VALUE_LENGTH) { int c = str.charAt(0); c -= '0'; if (c == 0) { // Note that 00,01 etc. are not valid Uint32 presentations return (len == 1) ? 0L : -1L; } if (1 <= c && c <= 9) { long v = c; for (int i = 1; i != len; ++i) { c = str.charAt(i) - '0'; if (!(0 <= c && c <= 9)) { return -1; } v = 10 * v + c; } // Check for overflow if ((v >>> 32) == 0) { return v; } } } return -1; } static boolean isSpecialProperty(String s) { return s.equals("__proto__") || s.equals("__parent__"); } // ------------------ // Statements // ------------------ public static String getMessage0(String messageId) { return getMessage(messageId, null); }

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS>p[0], null, 0); } public static EcmaError constructError(String error, String message, String sourceName, int lineNumber, String lineSource, int columnNumber) { return new EcmaError(error, message, sourceName, lineNumber, lineSource, columnNumber); } public static EcmaError typeError(String message) { return constructError("TypeError", message); } public static EcmaError typeError0(String messageId) { String msg = getMessage0(messageId); return typeError(msg); } public static EcmaError typeError1(String messageId, String arg1) { String msg = getMessage1(messageId, arg1); return typeError(msg); } public static EcmaError typeError2(String messageId, String arg1, String arg2) { String msg = getMessage2(messageId, arg1, arg2); return typeError(msg); } public static EcmaError typeError3(String messageId, String arg1, String arg2, String arg3) { String msg = getMessage3(messageId, arg1, arg2, arg3); return typeError(msg); } public static RuntimeException undefReadError(Object object, Object id) { String idStr = (id == null) ? "null" : id.toString(); return typeError2("msg.undef.prop.read", toString(object), idStr); } public static RuntimeException undefCallError(Object object, Object id) { String idStr = (id == null) ? "null" : id.toString(); return typeError2("msg.undef.method.call", toString(object), idStr); } public static RuntimeException undefWriteError(Object object, Object id, Object value) { String idStr = (id == null) ? "null" : id.toString(); String valueStr = toString(value); return typeError3("msg.undef.prop.write", toString(object), idStr, valueStr); } public static RuntimeException notFunctionError(Object value) { return notFunctionError(value, value); } public static RuntimeException notFunctionError(Object value, Object message

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS>Helper) { // XXX Use value for better error reporting String msg = (messageHelper == null) ? "null" : messageHelper.toString(); return typeError2("msg.isnt.function", msg, value == null ? "null" : value.getClass().getName()); } static int lastIndexResult(Context cx) { return cx.scratchIndex; } public static void storeUint32Result(Context cx, long value) { if ((value >>> 32) != 0) throw new IllegalArgumentException(); cx.scratchUint32 = value; } public static long lastUint32Result(Context cx) { long value = cx.scratchUint32; if ((value >>> 32) != 0) throw new IllegalStateException(); return value; } static String makeUrlForGeneratedScript (boolean isEval, String masterScriptUrl, int masterScriptLine) { if (isEval) { return masterScriptUrl+'#'+masterScriptLine+"(eval)"; } else { return masterScriptUrl+'#'+masterScriptLine+"(Function)"; } } static boolean isGeneratedScript(String sourceUrl) { // ALERT: this may clash with a valid URL containing (eval) or // (Function) return sourceUrl.indexOf("(eval)") >= 0 || sourceUrl.indexOf("(Function)") >= 0; } public static final Object[] emptyArgs = new Object[0]; public static final String[] emptyStrings = new String[0]; }

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS> // memory footprint associated with these (similar to IRFactory). private final Node templateNode; private class ErrorReporterParser { void addParserWarning(String messageId, String messageArg, int lineno, int charno) { errorReporter.warning(ScriptRuntime.getMessage1(messageId, messageArg), sourceName, lineno, null, charno); } void addParserWarning(String messageId, int lineno, int charno) { errorReporter.warning(ScriptRuntime.getMessage0(messageId), sourceName, lineno, null, charno); } void addTypeWarning(String messageId, String messageArg, int lineno, int charno) { errorReporter.warning( "Bad type annotation. " + ScriptRuntime.getMessage1(messageId, messageArg), sourceName, lineno, null, charno); } void addTypeWarning(String messageId, int lineno, int charno) { errorReporter.warning( "Bad type annotation. " + ScriptRuntime.getMessage0(messageId), sourceName, lineno, null, charno); } } // The DocInfo with the fileoverview tag for the whole file. private JSDocInfo fileOverviewJSDocInfo = null; private State state; private final Map<String, Annotation> annotationNames; private final Set<String> suppressionNames; static private final Set<String> modifiesAnnotationKeywords = ImmutableSet.<String>of("this", "arguments"); private Node.FileLevelJsDocBuilder fileLevelJsDocBuilder; /** * Sets the JsDocBuilder for the file-level (root) node of this parse. The * parser uses the builder to append any preserve annotations it encounters * in jsdoc comments. * * @param fileLevelJsDocBuilder */ void setFileLevelJsDocBuilder( Node.FileLevelJsDocBuilder fileLevelJsDocBuilder) { this.fileLevelJsDocBuilder = fileLevelJsDocBuilder; } /** * Sets the file overview JSDocInfo, in order to warn about multiple uses of * the @fileoverview tag in a file. */ void setFileOverviewJSDocInfo(JSDocInfo fileOverviewJSDocInfo) { this.fileOverviewJSDocInfo = fileOverview

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS> token = blockInfo.token; if (!blockInfo.string.isEmpty()) { jsdocBuilder.recordBlockDescription(blockInfo.string); } } else { if (token != JsDocToken.ANNOTATION && token != JsDocToken.EOC) { // Mark that there was a description, but don't bother marking // what it was. jsdocBuilder.recordBlockDescription(""); } } // Parse the actual JsDoc. retry: for (;;) { switch (token) { case ANNOTATION: if (state == State.SEARCHING_ANNOTATION) { state = State.SEARCHING_NEWLINE; lineno = stream.getLineno(); charno = stream.getCharno(); String annotationName = stream.getString(); Annotation annotation = annotationNames.get(annotationName); if (annotation == null) { parser.addParserWarning("msg.bad.jsdoc.tag", annotationName, stream.getLineno(), stream.getCharno()); } else { // Mark the beginning of the annotation. jsdocBuilder.markAnnotation(annotationName, lineno, charno); switch (annotation) { case AUTHOR: if (jsdocBuilder.shouldParseDocumentation()) { ExtractionInfo authorInfo = extractSingleLineBlock(); String author = authorInfo.string; if (author.length() == 0) { parser.addParserWarning("msg.jsdoc.authormissing", stream.getLineno(), stream.getCharno()); } else { jsdocBuilder.addAuthor(author); } token = authorInfo.token; } else { token = eatTokensUntilEOL(token); } continue retry; case CONSTANT: if (!jsdocBuilder.recordConstancy()) { parser.addParserWarning("msg.jsdoc.const", stream.getLineno(), stream.getCharno()); } token = eatTokensUntilEOL(); continue retry; case CONSTRUCTOR: if (!jsdocBuilder.recordConstructor()) { if (jsdocBuilder.isInterfaceRecorded()) { parser.addTypeWarning("msg.jsdoc.interface.constructor", stream.getLineno(), stream.getCharno()); } else { parser.add

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS>TypeWarning("msg.jsdoc.incompat.type", stream.getLineno(), stream.getCharno()); } } token = eatTokensUntilEOL(); continue retry; case DEPRECATED: if (!jsdocBuilder.recordDeprecated()) { parser.addParserWarning("msg.jsdoc.deprecated", stream.getLineno(), stream.getCharno()); } // Find the reason/description, if any. ExtractionInfo reasonInfo = extractMultilineTextualBlock(token); String reason = reasonInfo.string; if (reason.length() > 0) { jsdocBuilder.recordDeprecationReason(reason); } token = reasonInfo.token; continue retry; case INTERFACE: if (!jsdocBuilder.recordInterface()) { if (jsdocBuilder.isConstructorRecorded()) { parser.addTypeWarning("msg.jsdoc.interface.constructor", stream.getLineno(), stream.getCharno()); } else { parser.addTypeWarning("msg.jsdoc.incompat.type", stream.getLineno(), stream.getCharno()); } } token = eatTokensUntilEOL(); continue retry; case DESC: if (jsdocBuilder.isDescriptionRecorded()) { parser.addParserWarning("msg.jsdoc.desc.extra", stream.getLineno(), stream.getCharno()); token = eatTokensUntilEOL(); continue retry; } else { ExtractionInfo descriptionInfo = extractMultilineTextualBlock(token); String description = descriptionInfo.string; jsdocBuilder.recordDescription(description); token = descriptionInfo.token; continue retry; } case FILE_OVERVIEW: String fileOverview = ""; if (jsdocBuilder.shouldParseDocumentation()) { ExtractionInfo fileOverviewInfo = extractMultilineTextualBlock(token, WhitespaceOption.TRIM); fileOverview = fileOverviewInfo.string; token = fileOverviewInfo.token; } else { token = eatTokensUntilEOL(token); } if (!jsdocBuilder.recordFileOverview(fileOverview) || fileOverviewJSDocInfo != null) { parser.addParserWarning("msg.jsdoc.fileoverview.extra

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS>", stream.getLineno(), stream.getCharno()); } continue retry; case LICENSE: case PRESERVE: ExtractionInfo preserveInfo = extractMultilineTextualBlock(token, WhitespaceOption.PRESERVE); String preserve = preserveInfo.string; if (preserve.length() > 0) { if (fileLevelJsDocBuilder != null) { fileLevelJsDocBuilder.append(preserve); } } token = preserveInfo.token; continue retry; case ENUM: token = next(); lineno = stream.getLineno(); charno = stream.getCharno(); type = null; if (token != JsDocToken.EOL && token != JsDocToken.EOC) { type = createJSTypeExpression( parseAndRecordTypeNode(token)); } if (type == null) { type = createJSTypeExpression(newStringNode("number")); } if (!jsdocBuilder.recordEnumParameterType(type)) { parser.addTypeWarning( "msg.jsdoc.incompat.type", lineno, charno); } token = eatTokensUntilEOL(token); continue retry; case EXPORT: if (!jsdocBuilder.recordExport()) { parser.addParserWarning("msg.jsdoc.export", stream.getLineno(), stream.getCharno()); } token = eatTokensUntilEOL(); continue retry; case EXTERNS: if (!jsdocBuilder.recordExterns()) { parser.addParserWarning("msg.jsdoc.externs", stream.getLineno(), stream.getCharno()); } token = eatTokensUntilEOL(); continue retry; case JAVA_DISPATCH: if (!jsdocBuilder.recordJavaDispatch()) { parser.addParserWarning("msg.jsdoc.javadispatch", stream.getLineno(), stream.getCharno()); } token = eatTokensUntilEOL(); continue retry; case EXTENDS: case IMPLEMENTS: skipEOLs(); token = next(); lineno = stream.getLineno(); charno = stream.getCharno(); boolean matchingRc = false; if (token == JsDocToken.LC) { token

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS> = next(); lineno = stream.getLineno(); charno = stream.getCharno(); type = null; if (token == JsDocToken.LC) { type = createJSTypeExpression( parseAndRecordTypeNode(token)); if (type == null) { // parsing error reported during recursive descent // recovering parsing token = eatTokensUntilEOL(); continue retry; } } // *Update* the token to that after the type annotation. token = current(); // Save the throw type. jsdocBuilder.recordThrowType(type); // Find the throw's description (if applicable). if (jsdocBuilder.shouldParseDocumentation()) { ExtractionInfo descriptionInfo = extractMultilineTextualBlock(token); String description = descriptionInfo.string; if (description.length() > 0) { jsdocBuilder.recordThrowDescription(type, description); } token = descriptionInfo.token; } else { token = eatTokensUntilEOL(token); } continue retry; case PARAM: skipEOLs(); token = next(); lineno = stream.getLineno(); charno = stream.getCharno(); type = null; if (token == JsDocToken.LC) { type = createJSTypeExpression( parseAndRecordParamTypeNode(token)); if (type == null) { // parsing error reported during recursive descent // recovering parsing token = eatTokensUntilEOL(); continue retry; } skipEOLs(); token = next(); lineno = stream.getLineno(); charno = stream.getCharno(); } String name = null; boolean isBracketedParam = JsDocToken.LB == token; if (isBracketedParam) { token = next(); } if (JsDocToken.STRING != token) { parser.addTypeWarning("msg.missing.variable.name", lineno, charno); } else { name = stream.getString(); if (isBracketedParam) { token = next(); // Throw out JsDocToolkit's "default" parameter // annotation. It makes no sense under our type // system. if (JsDocToken.EQUALS == token) { token =

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS> next(); if (JsDocToken.STRING == token) { token = next(); } } if (JsDocToken.RB != token) { reportTypeSyntaxWarning("msg.jsdoc.missing.rb"); } else if (type != null) { // Make the type expression optional, if it isn't // already. type = JSTypeExpression.makeOptionalArg(type); } } // If the param name has a DOT in it, just throw it out // quietly. We do not handle the JsDocToolkit method // for handling properties of params. if (name.indexOf('.') > -1) { name = null; } else if (!jsdocBuilder.recordParameter(name, type)) { if (jsdocBuilder.hasParameter(name)) { parser.addTypeWarning("msg.dup.variable.name", name, lineno, charno); } else { parser.addTypeWarning("msg.jsdoc.incompat.type", name, lineno, charno); } } } if (name == null) { token = eatTokensUntilEOL(token); continue retry; } jsdocBuilder.markName(name, lineno, charno); // Find the parameter's description (if applicable). if (jsdocBuilder.shouldParseDocumentation()) { ExtractionInfo paramDescriptionInfo = extractMultilineTextualBlock(token); String paramDescription = paramDescriptionInfo.string; if (paramDescription.length() > 0) { jsdocBuilder.recordParameterDescription(name, paramDescription); } token = paramDescriptionInfo.token; } else { token = eatTokensUntilEOL(token); } continue retry; case PRESERVE_TRY: if (!jsdocBuilder.recordPreserveTry()) { parser.addParserWarning("msg.jsdoc.preservertry", stream.getLineno(), stream.getCharno()); } token = eatTokensUntilEOL(); continue retry; case PRIVATE: if (!jsdocBuilder.recordVisibility(Visibility.PRIVATE)) { parser.addParserWarning("msg.jsdoc.visibility.private", stream.getLineno(), stream.getCharno()); } token = eatTokensUntilEOL(); continue

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS> retry; case PROTECTED: if (!jsdocBuilder.recordVisibility(Visibility.PROTECTED)) { parser.addParserWarning("msg.jsdoc.visibility.protected", stream.getLineno(), stream.getCharno()); } token = eatTokensUntilEOL(); continue retry; case PUBLIC: if (!jsdocBuilder.recordVisibility(Visibility.PUBLIC)) { parser.addParserWarning("msg.jsdoc.visibility.public", stream.getLineno(), stream.getCharno()); } token = eatTokensUntilEOL(); continue retry; case NO_SHADOW: if (!jsdocBuilder.recordNoShadow()) { parser.addParserWarning("msg.jsdoc.noshadow", stream.getLineno(), stream.getCharno()); } token = eatTokensUntilEOL(); continue retry; case NO_SIDE_EFFECTS: if (!jsdocBuilder.recordNoSideEffects()) { parser.addParserWarning("msg.jsdoc.nosideeffects", stream.getLineno(), stream.getCharno()); } token = eatTokensUntilEOL(); continue retry; case MODIFIES: token = parseModifiesTag(next()); continue retry; case IMPLICIT_CAST: if (!jsdocBuilder.recordImplicitCast()) { parser.addTypeWarning("msg.jsdoc.implicitcast", stream.getLineno(), stream.getCharno()); } token = eatTokensUntilEOL(); continue retry; case SEE: if (jsdocBuilder.shouldParseDocumentation()) { ExtractionInfo referenceInfo = extractSingleLineBlock(); String reference = referenceInfo.string; if (reference.length() == 0) { parser.addParserWarning("msg.jsdoc.seemissing", stream.getLineno(), stream.getCharno()); } else { jsdocBuilder.addReference(reference); } token = referenceInfo.token; } else { token = eatTokensUntilEOL(token); } continue retry; case SUPPRESS: token = parseSuppressTag(next()); continue retry; case TEMPLATE: ExtractionInfo templateInfo = extractSingleLineBlock(); String templateTypeName = templateInfo.string; if (template

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS>TypeName.length() == 0) { parser.addTypeWarning("msg.jsdoc.templatemissing", stream.getLineno(), stream.getCharno()); } else if (!jsdocBuilder.recordTemplateTypeName( templateTypeName)) { parser.addTypeWarning("msg.jsdoc.template.at.most.once", stream.getLineno(), stream.getCharno()); } token = templateInfo.token; continue retry; case VERSION: ExtractionInfo versionInfo = extractSingleLineBlock(); String version = versionInfo.string; if (version.length() == 0) { parser.addParserWarning("msg.jsdoc.versionmissing", stream.getLineno(), stream.getCharno()); } else { if (!jsdocBuilder.recordVersion(version)) { parser.addParserWarning("msg.jsdoc.extraversion", stream.getLineno(), stream.getCharno()); } } token = versionInfo.token; continue retry; case DEFINE: case RETURN: case THIS: case TYPE: case TYPEDEF: lineno = stream.getLineno(); charno = stream.getCharno(); Node typeNode = null; if (!lookAheadForTypeAnnotation() && annotation == Annotation.RETURN) { // If RETURN doesn't have a type annotation, record // it as the unknown type. typeNode = newNode(Token.QMARK); } else { skipEOLs(); token = next(); typeNode = parseAndRecordTypeNode(token, lineno, charno); } if (annotation == Annotation.THIS) { typeNode = wrapNode(Token.BANG, typeNode); if (typeNode != null && token != JsDocToken.LC) { typeNode.putBooleanProp(Node.BRACELESS_TYPE, true); } } type = createJSTypeExpression(typeNode); if (type == null) { // error reported during recursive descent // recovering parsing } else { switch (annotation) { case DEFINE: if (!jsdocBuilder.recordDefineType(type)) { parser.addParserWarning("msg.jsdoc.define", lineno, charno); } break; case RETURN:

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS> if (!jsdocBuilder.recordReturnType(type)) { parser.addTypeWarning( "msg.jsdoc.incompat.type", lineno, charno); break; } // Find the return's description (if applicable). if (jsdocBuilder.shouldParseDocumentation()) { ExtractionInfo returnDescriptionInfo = extractMultilineTextualBlock(token); String returnDescription = returnDescriptionInfo.string; if (returnDescription.length() > 0) { jsdocBuilder.recordReturnDescription( returnDescription); } token = returnDescriptionInfo.token; } else { token = eatTokensUntilEOL(token); } continue retry; case THIS: if (!jsdocBuilder.recordThisType(type)) { parser.addTypeWarning( "msg.jsdoc.incompat.type", lineno, charno); } break; case TYPE: if (!jsdocBuilder.recordType(type)) { parser.addTypeWarning( "msg.jsdoc.incompat.type", lineno, charno); } break; case TYPEDEF: if (!jsdocBuilder.recordTypedef(type)) { parser.addTypeWarning( "msg.jsdoc.incompat.type", lineno, charno); } break; } token = eatTokensUntilEOL(); } continue retry; } } } break; case EOC: if (hasParsedFileOverviewDocInfo()) { fileOverviewJSDocInfo = retrieveAndResetParsedJSDocInfo(); } return true; case EOF: // discard any accumulated information jsdocBuilder.build(null); parser.addParserWarning("msg.unexpected.eof", stream.getLineno(), stream.getCharno()); return false; case EOL: if (state == State.SEARCHING_NEWLINE) { state = State.SEARCHING_ANNOTATION; } token = next(); continue retry; default: if (token == JsDocToken.STAR && state == State.SEARCHING_ANNOTATION) { token = next(); continue retry; } else { state = State.SEARCHING_NEWLINE; token = eatTokensUntilEOL(); continue retry

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS> Whether the type expression starts with a "{". * @param onlyParseSimpleNames If true, only simple type names are parsed * (via a call to parseTypeNameAnnotation instead of * parseTypeExpressionAnnotation). * @return The type expression found or null if none. */ private Node parseAndRecordTypeNode(JsDocToken token, int lineno, int startCharno, boolean matchingLC, boolean onlyParseSimpleNames) { Node typeNode = null; if (onlyParseSimpleNames) { typeNode = parseTypeNameAnnotation(token); } else { typeNode = parseTypeExpressionAnnotation(token); } if (typeNode != null && !matchingLC) { typeNode.putBooleanProp(Node.BRACELESS_TYPE, true); } int endCharno = stream.getCharno(); jsdocBuilder.markTypeNode(typeNode, lineno, startCharno, endCharno, matchingLC); return typeNode; } /** * Converts a JSDoc token to its string representation. */ private String toString(JsDocToken token) { switch (token) { case ANNOTATION: return "@" + stream.getString(); case BANG: return "!"; case COMMA: return ","; case COLON: return ":"; case GT: return ">"; case LB: return "["; case LC: return "{"; case LP: return "("; case LT: return ".<"; case QMARK: return "?"; case PIPE: return "|"; case RB: return "]"; case RC: return "}"; case RP: return ")"; case STAR: return "*"; case ELLIPSIS: return "..."; case EQUALS: return "="; case STRING: return stream.getString(); default: throw new IllegalStateException(token.toString()); } } /** * Constructs a new {@code JSTypeExpression}. * @param n A node. May be null. */ private JSTypeExpression createJSTypeExpression(Node n) { return n == null ? null : new JSTypeExpression(n, sourceName); } /** * Tuple for returning both the string extracted and the * new token

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS> following a call to any of the extract*Block * methods. */ private static class ExtractionInfo { private final String string; private final JsDocToken token; public ExtractionInfo(String string, JsDocToken token) { this.string = string; this.token = token; } } /** * Extracts the text found on the current line starting at token. Note that * token = token.info; should be called after this method is used to update * the token properly in the parser. * * @return The extraction information. */ private ExtractionInfo extractSingleLineBlock() { // Get the current starting point. stream.update(); int lineno = stream.getLineno(); int charno = stream.getCharno() + 1; String line = stream.getRemainingJSDocLine().trim(); // Record the textual description. if (line.length() > 0) { jsdocBuilder.markText(line, lineno, charno, lineno, charno + line.length()); } return new ExtractionInfo(line, next()); } private ExtractionInfo extractMultilineTextualBlock(JsDocToken token) { return extractMultilineTextualBlock(token, WhitespaceOption.SINGLE_LINE); } private enum WhitespaceOption { /** * Preserves all whitespace and formatting. Needed for licenses and * purposely formatted text. */ PRESERVE, /** Preserves newlines but trims the output. */ TRIM, /** Removes newlines and turns the output into a single line string. */ SINGLE_LINE } /** * Extracts the text found on the current line and all subsequent * until either an annotation, end of comment or end of file is reached. * Note that if this method detects an end of line as the first token, it * will quit immediately (indicating that there is no text where it was * expected). Note that token = info.token; should be called after this * method is used to update the token properly in the parser. * * @param token The start token. * @param option How to handle whitespace. * * @return The extraction information. */ @SuppressWarnings("fallthrough")

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS> private ExtractionInfo extractMultilineTextualBlock(JsDocToken token, WhitespaceOption option) { if (token == JsDocToken.EOC || token == JsDocToken.EOL || token == JsDocToken.EOF) { return new ExtractionInfo("", token); } stream.update(); int startLineno = stream.getLineno(); int startCharno = stream.getCharno() + 1; // Read the content from the first line. String line = stream.getRemainingJSDocLine(); if (option != WhitespaceOption.PRESERVE) { line = line.trim(); } StringBuilder builder = new StringBuilder(); builder.append(line); state = State.SEARCHING_ANNOTATION; token = next(); boolean ignoreStar = false; do { switch (token) { case STAR: if (!ignoreStar) { if (builder.length() > 0) { builder.append(' '); } builder.append('*'); } token = next(); continue; case EOL: if (option != WhitespaceOption.SINGLE_LINE) { builder.append("\n"); } ignoreStar = true; token = next(); continue; case ANNOTATION: case EOC: case EOF: // When we're capturing a license block, annotations // in the block are ok. if (!(option == WhitespaceOption.PRESERVE && token == JsDocToken.ANNOTATION)) { String multilineText = builder.toString(); if (option != WhitespaceOption.PRESERVE) { multilineText = multilineText.trim(); } int endLineno = stream.getLineno(); int endCharno = stream.getCharno(); if (multilineText.length() > 0) { jsdocBuilder.markText(multilineText, startLineno, startCharno, endLineno, endCharno); } return new ExtractionInfo(multilineText, token); } // FALL THROUGH default: ignoreStar = false; state = State.SEARCHING_ANNOTATION; if (builder.length() > 0) { builder.append(' '); } builder.append

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS>(toString(token)); line = stream.getRemainingJSDocLine(); if (option != WhitespaceOption.PRESERVE) { line = trimEnd(line); } builder.append(line); token = next(); } } while (true); } /** * Extracts the top-level block comment from the JsDoc comment, if any. * This method differs from the extractMultilineTextualBlock in that it * terminates under different conditions (it doesn't have the same * prechecks), it does not first read in the remaining of the current * line and its conditions for ignoring the "*" (STAR) are different. * * @param token The starting token. * * @return The extraction information. */ private ExtractionInfo extractBlockComment(JsDocToken token) { StringBuilder builder = new StringBuilder(); boolean ignoreStar = true; do { switch (token) { case ANNOTATION: case EOC: case EOF: return new ExtractionInfo(builder.toString().trim(), token); case STAR: if (!ignoreStar) { if (builder.length() > 0) { builder.append(' '); } builder.append('*'); } token = next(); continue; case EOL: ignoreStar = true; builder.append('\n'); token = next(); continue; default: if (!ignoreStar && builder.length() > 0) { builder.append(' '); } ignoreStar = false; builder.append(toString(token)); String line = stream.getRemainingJSDocLine(); line = trimEnd(line); builder.append(line); token = next(); } } while (true); } /** * Trim characters from only the end of a string. * This method will remove all whitespace characters * (defined by Character.isWhitespace(char), in addition to the characters * provided, from the end of the provided string. * * @param s String to be trimmed * @return String with whitespace and characters in extraChars removed * from the end. */ private static String trimEnd(String s) { int trimCount = 0; while (trimCount < s.length())

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS> { char ch = s.charAt(s.length() - trimCount - 1); if (Character.isWhitespace(ch)) { trimCount++; } else { break; } } if (trimCount == 0) { return s; } return s.substring(0, s.length() - trimCount); } // Based on ES4 grammar proposed on July 10, 2008. // http://wiki.ecmascript.org/doku.php?id=spec:spec // Deliberately written to line up with the actual grammar rules, // for maximum flexibility. // TODO(nicksantos): The current implementation tries to maintain backwards // compatibility with previous versions of the spec whenever we can. // We should try to gradually withdraw support for these. /** * TypeExpressionAnnotation := TypeExpression | * '{' TopLevelTypeExpression '}' */ private Node parseTypeExpressionAnnotation(JsDocToken token) { if (token == JsDocToken.LC) { skipEOLs(); Node typeNode = parseTopLevelTypeExpression(next()); if (typeNode != null) { skipEOLs(); if (!match(JsDocToken.RC)) { reportTypeSyntaxWarning("msg.jsdoc.missing.rc"); } else { next(); } } return typeNode; } else { return parseTypeExpression(token); } } /** * ParamTypeExpressionAnnotation := * '{' OptionalParameterType '}' | * '{' TopLevelTypeExpression '}' | * '{' '...' TopLevelTypeExpression '}' * * OptionalParameterType := * TopLevelTypeExpression '=' */ private Node parseParamTypeExpressionAnnotation(JsDocToken token) { Preconditions.checkArgument(token == JsDocToken.LC); skipEOLs(); boolean restArg = false; token = next(); if (token == JsDocToken.ELLIPSIS) { token = next(); if (token == JsDocToken.RC) { // EMPTY represents the UNKNOWN type in the Type AST. return wrapNode(Token.ELLIPSIS, new Node(Token.EMPTY)); } restArg = true; } Node typeNode

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS> 'null' | 'undefined' | TypeName * | FunctionType | UnionType | RecordType | ArrayType */ private Node parseBasicTypeExpression(JsDocToken token) { if (token == JsDocToken.STAR) { return newNode(Token.STAR); } else if (token == JsDocToken.LB) { skipEOLs(); return parseArrayType(next()); } else if (token == JsDocToken.LC) { skipEOLs(); return parseRecordType(next()); } else if (token == JsDocToken.LP) { skipEOLs(); return parseUnionType(next()); } else if (token == JsDocToken.STRING) { String string = stream.getString(); if ("function".equals(string)) { skipEOLs(); return parseFunctionType(next()); } else if ("null".equals(string) || "undefined".equals(string)) { return newStringNode(string); } else { return parseTypeName(token); } } return reportGenericTypeSyntaxWarning(); } /** * TypeName := NameExpression | NameExpression TypeApplication * TypeApplication := '.<' TypeExpressionList '>' * TypeExpressionList := TypeExpression // a white lie */ private Node parseTypeName(JsDocToken token) { if (token != JsDocToken.STRING) { return reportGenericTypeSyntaxWarning(); } String typeName = stream.getString(); while (match(JsDocToken.EOL) && typeName.charAt(typeName.length() - 1) == '.') { skipEOLs(); if (match(JsDocToken.STRING)) { next(); typeName += stream.getString(); } } Node typeNameNode = newStringNode(typeName); if (match(JsDocToken.LT)) { next(); skipEOLs(); Node memberType = parseTypeExpressionList(next()); if (memberType != null) { typeNameNode.addChildToFront(memberType); skipEOLs(); if (!match(JsDocToken.GT)) { return reportTypeSyntaxWarning("msg.jsdoc.missing.gt"); } next(); } } return typeNameNode; } /** * FunctionType := 'function' FunctionSignatureType * FunctionSignatureType :=

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS> FieldType | FieldType ',' FieldTypeList */ private Node parseFieldTypeList(JsDocToken token) { Node fieldTypeList = newNode(Token.LB); do { Node fieldType = parseFieldType(token); if (fieldType == null) { return null; } fieldTypeList.addChildToBack(fieldType); skipEOLs(); if (!match(JsDocToken.COMMA)) { break; } // Move to the comma token. next(); // Move to the token passed the comma. skipEOLs(); token = next(); } while (true); return fieldTypeList; } /** * FieldType := FieldName | FieldName ':' TypeExpression */ private Node parseFieldType(JsDocToken token) { Node fieldName = parseFieldName(token); if (fieldName == null) { return null; } skipEOLs(); if (!match(JsDocToken.COLON)) { return fieldName; } // Move to the colon. next(); // Move to the token after the colon and parse // the type expression. skipEOLs(); Node typeExpression = parseTypeExpression(next()); if (typeExpression == null) { return null; } Node fieldType = newNode(Token.COLON); fieldType.addChildToBack(fieldName); fieldType.addChildToBack(typeExpression); return fieldType; } /** * FieldName := NameExpression | StringLiteral | NumberLiteral | * ReservedIdentifier */ private Node parseFieldName(JsDocToken token) { switch (token) { case STRING: String string = stream.getString(); return newStringNode(string); default: return null; } } private Node wrapNode(int type, Node n) { return n == null ? null : new Node(type, n, stream.getLineno(), stream.getCharno()).clonePropsFrom(templateNode); } private Node newNode(int type) { return new Node(type, stream.getLineno(), stream.getCharno()).clonePropsFrom(templateNode); } private Node newStringNode(String s) { return Node.newString(s, stream.getLineno(), stream.getCharno()).clonePropsFrom(templateNode); } // This is

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS>Token token2) { unreadToken = next(); return unreadToken == token1 || unreadToken == token2; } /** * Gets the next token of the token stream or the buffered token if a matching * was previously made. */ private JsDocToken next() { if (unreadToken == NO_UNREAD_TOKEN) { return stream.getJsDocToken(); } else { return current(); } } /** * Gets the current token, invalidating it in the process. */ private JsDocToken current() { JsDocToken t = unreadToken; unreadToken = NO_UNREAD_TOKEN; return t; } /** * Skips all EOLs and all empty lines in the JSDoc. Call this method if you * want the JSDoc entry to span multiple lines. */ private void skipEOLs() { while (match(JsDocToken.EOL)) { next(); if (match(JsDocToken.STAR)) { next(); } } } /** * Determines whether the parser has been populated with docinfo with a * fileoverview tag. */ private boolean hasParsedFileOverviewDocInfo() { return jsdocBuilder.isPopulatedWithFileOverview(); } boolean hasParsedJSDocInfo() { return jsdocBuilder.isPopulated(); } JSDocInfo retrieveAndResetParsedJSDocInfo() { return jsdocBuilder.build(sourceName); } /** * Gets the fileoverview JSDocInfo, if any. */ JSDocInfo getFileOverviewJSDocInfo() { return fileOverviewJSDocInfo; } /** * Look ahead for a type annotation by advancing the character stream. * Does not modify the token stream. * This is kind of a hack, and is only necessary because we use the token * stream to parse types, but need the underlying character stream to get * JsDoc descriptions. * @return Whether we found a type annotation. */ private boolean lookAheadForTypeAnnotation() { boolean matchedLc = false; int c; while (true) { c = stream.getChar(); if (c == ' ') { continue; } else if (c == '{') {

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS>/* * Copyright 2007 The Closure Compiler Authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.google.javascript.jscomp; import com.google.javascript.rhino.Node; import com.google.javascript.rhino.jstype.FunctionType; import com.google.javascript.rhino.jstype.JSTypeRegistry; import com.google.javascript.rhino.jstype.ObjectType; import java.util.Collection; import java.util.Collections; import java.util.List; /** * CodingConvention defines a set of hooks to customize the behavior of the * Compiler for a specific team/company. * */ public class DefaultCodingConvention implements CodingConvention { private static final long serialVersionUID = 1L; @Override public boolean isConstant(String variableName) { return false; } @Override public boolean isConstantKey(String variableName) { return false; } @Override public boolean isValidEnumKey(String key) { return key != null && key.length() > 0; } @Override public boolean isOptionalParameter(Node parameter) { // be as lax as possible, but this must be mutually exclusive from // var_args parameters. return !isVarArgsParameter(parameter); } @Override public boolean isVarArgsParameter(Node parameter) { // be as lax as possible return parameter.getParent().getLastChild() == parameter; } @Override public boolean isExported(String name, boolean local) { return local && name.startsWith("$super"); } @Override public boolean isExported(String name) { return isExported(name, false) || isExported

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS>/* * Copyright 2007 The Closure Compiler Authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.google.javascript.jscomp.testing; import com.google.javascript.jscomp.mozilla.rhino.ErrorReporter; import com.google.javascript.jscomp.mozilla.rhino.EvaluatorException; import junit.framework.Assert; /** * <p>An error reporter for testing that verifies that messages reported to the * reporter are expected.</p> * * <p>Sample use</p> * <pre> * TestErrorReporter e = * new TestErrorReporter(null, new String[] { "first warning" }); * ... * assertTrue(e.hasEncounteredAllWarnings()); * </pre> * */ public final class TestErrorReporter extends Assert implements ErrorReporter { private final String[] errors; private final String[] warnings; private int errorsIndex = 0; private int warningsIndex = 0; public TestErrorReporter(String[] errors, String[] warnings) { this.errors = errors; this.warnings = warnings; } public void error(String message, String sourceName, int line, String lineSource, int lineOffset) { if (errors != null && errorsIndex < errors.length) { assertEquals(errors[errorsIndex++], message); } else { fail("extra error: " + message); } } public void warning(String message, String sourceName, int line, String lineSource, int lineOffset) { if (warnings != null && warningsIndex < warnings.length) { assertEquals(warnings[warningsIndex++], message); } else { fail("extra warning: " +

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS> message); } } public EvaluatorException runtimeError(String message, String sourceName, int line, String lineSource, int lineOffset) { return new EvaluatorException("JSCompiler test code: " + message); } /** * Returns whether all warnings were reported to this reporter. */ public boolean hasEncounteredAllWarnings() { return (warnings == null) ? warningsIndex == 0 : warnings.length == warningsIndex; } /** * Returns whether all errors were reported to this reporter. */ public boolean hasEncounteredAllErrors() { return (errors == null) ? errorsIndex == 0 : errors.length == errorsIndex; } }

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS>/* * Copyright 2009 The Closure Compiler Authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.google.javascript.jscomp; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Charsets; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.nio.charset.Charset; /** * An abstract representation of a JavaScript source file, as input to * JSCompiler. * * @author nicksantos@google.com (Nick Santos) * @author moedinger@google.com (Andrew Moedinger) */ public class JSSourceFile extends SourceFile { public static JSSourceFile fromFile(String fileName, Charset charSet) { return new JSSourceFile(SourceFile.fromFile(fileName, charSet)); } public static JSSourceFile fromFile(String fileName) { return new JSSourceFile(SourceFile.fromFile(fileName, Charsets.UTF_8)); } public static JSSourceFile fromFile(File file, Charset charSet) { return new JSSourceFile(SourceFile.fromFile(file, charSet)); } public static JSSourceFile fromFile(File file) { return new JSSourceFile(SourceFile.fromFile(file, Charsets.UTF_8)); } public static JSSourceFile fromCode(String fileName, String code) { return new JSSourceFile(SourceFile.fromCode(fileName, code)); } public static JSSourceFile fromInputStream(String fileName, InputStream s) throws IOException { return new JSSourceFile(SourceFile.fromInputStream

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS>(fileName, s)); } public static JSSourceFile fromGenerator(String fileName, Generator generator) { return new JSSourceFile(SourceFile.fromGenerator(fileName, generator)); } private SourceFile referenced; private JSSourceFile(SourceFile referenced) { super(referenced.getName()); this.referenced = referenced; } @Override public String getCode() throws IOException { return referenced.getCode(); } @Override public void clearCachedSource() { referenced.clearCachedSource(); } @Override @VisibleForTesting String getCodeNoCache() { return referenced.getCodeNoCache(); } }

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS>Stub) { // Incomplete definition Definition definition = new ExternalNameOnlyDefinition(node); nameDefinitionMultimap.put(name, definition); definitionSiteMap.put(node, new DefinitionSite(node, definition, traversal.getModule(), traversal.inGlobalScope(), inExterns)); } } } } /** * @return Whether the node has a JSDoc that actually declares something. */ private boolean jsdocContainsDeclarations(Node node) { JSDocInfo info = node.getJSDocInfo(); return (info != null && info.containsDeclaration()); } } private class UseSiteGatheringCallback extends AbstractPostOrderCallback { @Override public void visit(NodeTraversal traversal, Node node, Node parent) { Collection<Definition> defs = getDefinitionsReferencedAt(node); if (defs == null) { return; } Definition first = defs.iterator().next(); String name = getSimplifiedName(first.getLValue()); Preconditions.checkNotNull(name); nameUseSiteMultimap.put( name, new UseSite(node, traversal.getModule())); } } /** * @param use A use site to check. * @return Whether the use is a call or new. */ static boolean isCallOrNewSite(UseSite use) { Node call = use.node.getParent(); if (call == null) { // The node has been removed from the AST. return false; } // We need to make sure we're dealing with a call to the function we're // optimizing. If the the first child of the parent is not the site, this // is a nested call and it's a call to another function. return NodeUtil.isCallOrNew(call) && call.getFirstChild() == use.node; } /** * @return Whether the definition is directly exported. */ static boolean maybeExported( AbstractCompiler compiler, Definition definition) { // Assume an exported method result is used. Node lValue = definition.getLValue(); if (lValue == null) { return true; } String partialName; if (NodeUtil.isGetProp(lValue)) { partialName = lValue.getLastChild().getString

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS> if (!isConnected(n1, edge, n2)) { connect(n1, edge, n2); } } /** * Gets a node from the graph given a value. New nodes are created if that * value has not been assigned a graph node. Values equality are compared * using <code>Object.equals</code>. * * @param value The node's value. * @return The corresponding node in the graph. */ public abstract GraphNode<N, E> createNode(N value); /** Gets an immutable list of all nodes. */ public abstract Collection<GraphNode<N, E>> getNodes(); /** Gets an immutable list of all edges. */ public abstract List<GraphEdge<N, E>> getEdges(); /** * Gets the degree of a node. * * @param value The node's value. * @return The degree of the node. */ public abstract int getNodeDegree(N value); public int getWeight(N value) { return getNodeDegree(value); } /** * Gets the neighboring nodes. * * @param value The node's value. * @return A list of neighboring nodes. */ public abstract List<GraphNode<N, E>> getNeighborNodes(N value); public abstract Iterator<GraphNode<N, E>> getNeighborNodesIterator(N value); /** * Retrieves an edge from the graph. * * @param n1 Node one. * @param n2 Node two. * @return The list of edges between those two values in the graph. */ public abstract List<GraphEdge<N, E>> getEdges(N n1, N n2); /** * Retrieves any edge from the graph. * * @param n1 Node one. * @param n2 Node two. * @return The first edges between those two values in the graph. null if * there are none. */ public abstract GraphEdge<N, E> getFirstEdge(N n1, N n2); /** * Checks whether the node exists in the graph ({@link #createNode(Object)} * has been called with that value). * * @param n Node. * @return <code>true</code> if

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS>. */ public final void pushEdgeAnnotations() { if (edgeAnnotationStack == null) { edgeAnnotationStack = Lists.newLinkedList(); } pushAnnotations(edgeAnnotationStack, getEdges()); } /** * Restores edges' annotation values to state before last * {@link #pushEdgeAnnotations()}. */ public final void popEdgeAnnotations() { Preconditions.checkNotNull(edgeAnnotationStack, "Popping edge annotations without pushing."); popAnnotations(edgeAnnotationStack); } /** * A generic edge. * * @param <N> Value type that the graph node stores. * @param <E> Value type that the graph edge stores. */ public interface GraphEdge<N, E> extends Annotatable { /** * Retrieves the edge's value. * * @return The value. */ E getValue(); GraphNode<N, E> getNodeA(); GraphNode<N, E> getNodeB(); } /** * A simple implementation of SubGraph that calculates adjacency by iterating * over a node's neighbors. */ class SimpleSubGraph<N, E> implements SubGraph<N, E> { private Graph<N, E> graph; private List<GraphNode<N, E>> nodes = Lists.newArrayList(); SimpleSubGraph(Graph<N, E> graph) { this.graph = graph; } public boolean isIndependentOf(N value) { GraphNode<N, E> node = graph.getNode(value); for (GraphNode<N, E> n : nodes) { if (graph.getNeighborNodes(n.getValue()).contains(node)) { return false; } } return true; } public void addNode(N value) { nodes.add(graph.getNodeOrFail(value)); } } /** * Pushes a new list on stack and stores nodes annotations in the new list. * Clears objects' annotations as well. */ private static void pushAnnotations( Deque<GraphAnnotationState> stack, Collection<? extends Annotatable> haveAnnotations) { stack.push(new GraphAnnotationState(haveAnnotations.size())); for (Annotatable h : haveAnnotations) { stack.peek().add(new AnnotationState(h,

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS>ize("ERROR", Color.ERROR); case WARNING: return maybeColorize("WARNING", Color.WARNING); default: return level.toString(); } } private String maybeColorize(String text, Color color) { if (!colorize) return text; return color.getControlCharacter() + text + Color.RESET.getControlCharacter(); } }

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS>SError at a CheckLevel for a source file location. * Private to avoid any entanglement with code outside of the compiler. */ private JSError( String sourceName, @Nullable Node node, int lineno, int charno, DiagnosticType type, CheckLevel level, String... arguments) { this.type = type; this.node = node; this.description = type.format.format(arguments); this.lineNumber = lineno; this.charno = charno; this.sourceName = sourceName; this.level = level == null ? type.level : level; } /** * Creates a JSError for a source file location. Private to avoid * any entanglement with code outside of the compiler. */ private JSError(String sourceName, @Nullable Node node, DiagnosticType type, String... arguments) { this(sourceName, node, (node != null) ? node.getLineno() : -1, (node != null) ? node.getCharno() : -1, type, null, arguments); } public DiagnosticType getType() { return type; } /** * Format a message at the given level. * * @return the formatted message or {@code null} */ public String format(CheckLevel level, MessageFormatter formatter) { switch (level) { case ERROR: return formatter.formatError(this); case WARNING: return formatter.formatWarning(this); default: return null; } } @Override public String toString() { // TODO(user): remove custom toString. return type.key + ". " + description + " at " + (sourceName != null && sourceName.length() > 0 ? sourceName : "(unknown source)") + " line " + (lineNumber != -1 ? String.valueOf(lineNumber) : "(unknown line)") + " : " + (charno != -1 ? String.valueOf(charno) : "(unknown column)"); } /** * Get the character number. */ public int getCharno() { return charno; } @Override public boolean equals(Object o) { // Generated by Intellij IDEA if (this == o) { return true;

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS>/* * Copyright 2010 The Closure Compiler Authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.google.javascript.jscomp; import com.google.common.collect.Maps; import com.google.javascript.rhino.JSDocInfo; import com.google.javascript.rhino.Node; import com.google.javascript.rhino.Token; import java.util.Map; /** * Filters warnings based on in-code {@code @suppress} annotations. * @author nicksantos@google.com (Nick Santos) */ class SuppressDocWarningsGuard extends WarningsGuard { /** Warnings guards for each suppressable warnings group, indexed by name. */ private final Map<String, DiagnosticGroupWarningsGuard> suppressors = Maps.newHashMap(); /** * The suppressable groups, indexed by name. */ SuppressDocWarningsGuard(Map<String, DiagnosticGroup> suppressableGroups) { for (Map.Entry<String, DiagnosticGroup> entry : suppressableGroups.entrySet()) { suppressors.put( entry.getKey(), new DiagnosticGroupWarningsGuard( entry.getValue(), CheckLevel.OFF)); } } @Override public CheckLevel level(JSError error) { Node node = error.node; if (node != null) { for (Node current = node; current != null; current = current.getParent()) { int type = current.getType(); JSDocInfo info = null; // We only care about function annotations at the FUNCTION and SCRIPT // level. Otherwise, the @suppress annotation has an implicit // dependency on the exact structure of our AST, and that seems like

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS> // a bad idea. if (type == Token.FUNCTION) { info = NodeUtil.getFunctionInfo(current); } else if (type == Token.SCRIPT) { info = current.getJSDocInfo(); } if (info != null) { for (String suppressor : info.getSuppressions()) { WarningsGuard guard = suppressors.get(suppressor); // Some @suppress tags are for other tools, and // may not have a warnings guard. if (guard != null) { CheckLevel newLevel = guard.level(error); if (newLevel != null) { return newLevel; } } } } } } return null; } @Override public int getPriority() { // Happens after path-based filtering, but before other times // of filtering. return WarningsGuard.Priority.SUPPRESS_DOC.value; } }

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS>.google.javascript.jscomp.mozilla.rhino.ast.WhileLoop; import com.google.javascript.jscomp.mozilla.rhino.ast.WithStatement; /** * Type safe dispatcher interface for use with new-style Rhino ASTs. * * The contents of this file was generated using a script; it is * likely that the implementation below really belongs in a virtual * typeSafeProcess(TypeSafeDispatcher) method implemented by all AST * classes - which would make switching based on types and casting * unnecessary. * */ abstract class TypeSafeDispatcher<T> { abstract T processArrayLiteral(ArrayLiteral literalNode); abstract T processAssignment(Assignment assignmentNode); abstract T processAstRoot(AstRoot rootNode); abstract T processBlock(Block blockNode); abstract T processBreakStatement(BreakStatement statementNode); abstract T processCatchClause(CatchClause clauseNode); abstract T processConditionalExpression(ConditionalExpression exprNode); abstract T processContinueStatement(ContinueStatement statementNode); abstract T processDoLoop(DoLoop loopNode); abstract T processElementGet(ElementGet getNode); abstract T processEmptyExpression(EmptyExpression exprNode); abstract T processExpressionStatement(ExpressionStatement statementNode); abstract T processForInLoop(ForInLoop loopNode); abstract T processForLoop(ForLoop loopNode); abstract T processFunctionCall(FunctionCall callNode); abstract T processFunctionNode(FunctionNode functionNode); abstract T processIfStatement(IfStatement statementNode); abstract T processInfixExpression(InfixExpression exprNode); abstract T processKeywordLiteral(KeywordLiteral literalNode); abstract T processLabel(Label labelNode); abstract T processLabeledStatement(LabeledStatement statementNode); abstract T processName(Name nameNode); abstract T processNewExpression(NewExpression exprNode); abstract T processNumberLiteral(NumberLiteral literalNode); abstract T processObjectLiteral(ObjectLiteral literalNode); abstract T processObjectProperty(ObjectProperty propertyNode); abstract T processParenthesizedExpression(ParenthesizedExpression exprNode); abstract T processPropertyGet(PropertyGet getNode); abstract T processRegExpLiteral(RegExpLiteral literalNode); abstract T processReturnStatement(ReturnStatement statementNode); abstract T processScope(Scope scopeNode); abstract T processStringLiteral(StringLiteral literalNode);

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS>((PropertyGet) node); case Token.HOOK: return processConditionalExpression((ConditionalExpression) node); case Token.IF: return processIfStatement((IfStatement) node); case Token.LABEL: return processLabel((Label) node); case Token.LP: return processParenthesizedExpression((ParenthesizedExpression) node); case Token.NAME: return processName((Name) node); case Token.NEW: return processNewExpression((NewExpression) node); case Token.NUMBER: return processNumberLiteral((NumberLiteral) node); case Token.OBJECTLIT: return processObjectLiteral((ObjectLiteral) node); case Token.REGEXP: return processRegExpLiteral((RegExpLiteral) node); case Token.RETURN: return processReturnStatement((ReturnStatement) node); case Token.SCRIPT: return processAstRoot((AstRoot) node); case Token.STRING: return processStringLiteral((StringLiteral) node); case Token.SWITCH: return processSwitchStatement((SwitchStatement) node); case Token.THROW: return processThrowStatement((ThrowStatement) node); case Token.TRY: return processTryStatement((TryStatement) node); case Token.CONST: case Token.VAR: if (node instanceof VariableDeclaration) { return processVariableDeclaration((VariableDeclaration) node); } else if (node instanceof VariableInitializer) { return processVariableInitializer((VariableInitializer) node); } else { throw new IllegalStateException("Unexpected node type. class: " + node.getClass() + " type: " + Token.typeToName(node.getType())); } case Token.WHILE: return processWhileLoop((WhileLoop) node); case Token.WITH: return processWithStatement((WithStatement) node); } return processIllegalToken(node); } }

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS>/* * Copyright 2004 The Closure Compiler Authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.google.javascript.jscomp; import com.google.common.base.Preconditions; import com.google.common.base.Predicate; import com.google.common.base.Predicates; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Maps; import com.google.javascript.rhino.JSDocInfo; import com.google.javascript.rhino.Node; import com.google.javascript.rhino.Token; import com.google.javascript.rhino.TokenStream; import com.google.javascript.rhino.jstype.FunctionType; import com.google.javascript.rhino.jstype.JSType; import com.google.javascript.rhino.jstype.TernaryValue; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import javax.annotation.Nullable; /** * NodeUtil contains utilities that get properties from the Node object. * */ public final class NodeUtil { static final long MAX_POSITIVE_INTEGER_NUMBER = (long)Math.pow(2, 53); final static String JSC_PROPERTY_NAME_FN = "JSCompiler_renameProperty"; // TODO(user): Eliminate this class and make all of the static methods // instance methods of com.google.javascript.rhino.Node. /** the set of builtin constructors that don't have

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS> side effects. */ private static final Set<String> CONSTRUCTORS_WITHOUT_SIDE_EFFECTS = new HashSet<String>(Arrays.asList( "Array", "Date", "Error", "Object", "RegExp", "XMLHttpRequest")); // Utility class; do not instantiate. private NodeUtil() {} /** * Gets the boolean value of a node that represents a expression. This method * effectively emulates the <code>Boolean()</code> JavaScript cast function. * Note: unlike getBooleanValue this function does not return UNKNOWN * for expressions with side-effects. */ static TernaryValue getImpureBooleanValue(Node n) { switch (n.getType()) { case Token.ASSIGN: case Token.COMMA: // For ASSIGN and COMMA the value is the value of the RHS. return getImpureBooleanValue(n.getLastChild()); case Token.NOT: TernaryValue value = getImpureBooleanValue(n.getLastChild()); return value.not(); case Token.AND: { TernaryValue lhs = getImpureBooleanValue(n.getFirstChild()); TernaryValue rhs = getImpureBooleanValue(n.getLastChild()); return lhs.and(rhs); } case Token.OR: { TernaryValue lhs = getImpureBooleanValue(n.getFirstChild()); TernaryValue rhs = getImpureBooleanValue(n.getLastChild()); return lhs.or(rhs); } case Token.HOOK: { TernaryValue trueValue = getImpureBooleanValue( n.getFirstChild().getNext()); TernaryValue falseValue = getImpureBooleanValue(n.getLastChild()); if (trueValue.equals(falseValue)) { return trueValue; } else { return TernaryValue.UNKNOWN; } } case Token.ARRAYLIT: case Token.OBJECTLIT: // ignoring side-effects return TernaryValue.TRUE; default: return getPureBooleanValue(n); } } /** * Gets the boolean value of a node that represents a literal. This method * effectively emulates the <code>Boolean()</code> JavaScript cast function * except it return UNKNOWN for known values with side-effects, use * getExpressionBoolean

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS>Value if you don't care about side-effects. */ static TernaryValue getPureBooleanValue(Node n) { switch (n.getType()) { case Token.STRING: return TernaryValue.forBoolean(n.getString().length() > 0); case Token.NUMBER: return TernaryValue.forBoolean(n.getDouble() != 0); case Token.NOT: return getPureBooleanValue(n.getLastChild()).not(); case Token.NULL: case Token.FALSE: case Token.VOID: return TernaryValue.FALSE; case Token.NAME: String name = n.getString(); if ("undefined".equals(name) || "NaN".equals(name)) { // We assume here that programs don't change the value of the keyword // undefined to something other than the value undefined. return TernaryValue.FALSE; } else if ("Infinity".equals(name)) { return TernaryValue.TRUE; } break; case Token.TRUE: case Token.REGEXP: return TernaryValue.TRUE; case Token.ARRAYLIT: case Token.OBJECTLIT: if (!mayHaveSideEffects(n)) { return TernaryValue.TRUE; } } return TernaryValue.UNKNOWN; } /** * Gets the value of a node as a String, or null if it cannot be converted. * When it returns a non-null String, this method effectively emulates the * <code>String()</code> JavaScript cast function. */ static String getStringValue(Node n) { // TODO(user): regex literals as well. switch (n.getType()) { case Token.STRING: return n.getString(); case Token.NAME: String name = n.getString(); if ("undefined".equals(name) || "Infinity".equals(name) || "NaN".equals(name)) { return name; } break; case Token.NUMBER: return getStringValue(n.getDouble()); case Token.FALSE: case Token.TRUE: case Token.NULL: return Node.tokenToName(n.getType()); case Token.VOID: return "undefined"; case Token.NOT: TernaryValue child = getPureBooleanValue(n

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS>.getFirstChild()); if (child != TernaryValue.UNKNOWN) { return child.toBoolean(true) ? "false" : "true"; // reversed. } break; case Token.ARRAYLIT: return arrayToString(n); case Token.OBJECTLIT: return "[object Object]"; } return null; } static String getStringValue(double value) { long longValue = (long) value; // Return "1" instead of "1.0" if (longValue == value) { return Long.toString(longValue); } else { return Double.toString(value); } } /** * When converting arrays to string using Array.prototype.toString or * Array.prototype.join, the rules for conversion to String are different * than converting each element individually. Specifically, "null" and * "undefined" are converted to an empty string. * @param n A node that is a member of an Array. * @return The string representation. */ static String getArrayElementStringValue(Node n) { return (NodeUtil.isNullOrUndefined(n) || n.getType() == Token.EMPTY) ? "" : getStringValue(n); } static String arrayToString(Node literal) { Node first = literal.getFirstChild(); StringBuilder result = new StringBuilder(); int nextSlot = 0; int nextSkipSlot = 0; for (Node n = first; n != null; n = n.getNext()) { String childValue = getArrayElementStringValue(n); if (childValue == null) { return null; } if (n != first) { result.append(','); } result.append(childValue); nextSlot++; } return result.toString(); } /** * Gets the value of a node as a Number, or null if it cannot be converted. * When it returns a non-null Double, this method effectively emulates the * <code>Number()</code> JavaScript cast function. */ static Double getNumberValue(Node n) { switch (n.getType()) { case Token.TRUE: return 1.0; case Token.FALSE: case Token.NULL: return 0.0; case Token.NUMBER:

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS> return n.getDouble(); case Token.VOID: if (mayHaveSideEffects(n.getFirstChild())) { return null; } else { return Double.NaN; } case Token.NAME: // Check for known constants String name = n.getString(); if (name.equals("undefined")) { return Double.NaN; } if (name.equals("NaN")) { return Double.NaN; } if (name.equals("Infinity")) { return Double.POSITIVE_INFINITY; } return null; case Token.NEG: if (n.getChildCount() == 1 && n.getFirstChild().getType() == Token.NAME && n.getFirstChild().getString().equals("Infinity")) { return Double.NEGATIVE_INFINITY; } return null; case Token.NOT: TernaryValue child = getPureBooleanValue(n.getFirstChild()); if (child != TernaryValue.UNKNOWN) { return child.toBoolean(true) ? 0.0 : 1.0; // reversed. } break; case Token.STRING: return getStringNumberValue(n.getString()); case Token.ARRAYLIT: case Token.OBJECTLIT: String value = getStringValue(n); return value != null ? getStringNumberValue(value) : null; } return null; } static Double getStringNumberValue(String rawJsString) { if (rawJsString.contains("\u000b")) { // vertical tab is not always whitespace return null; } String s = trimJsWhiteSpace(rawJsString); // return ScriptRuntime.toNumber(s); if (s.length() == 0) { return 0.0; } if (s.length() > 2 && s.charAt(0) == '0' && (s.charAt(1) == 'x' || s.charAt(1) == 'X')) { // Attempt to convert hex numbers. try { return Double.valueOf(Integer.parseInt(s.substring(2), 16)); } catch (NumberFormatException e) { return Double.NaN; } } if (s.length() > 3 && (s.charAt(0) == '-' || s

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS>.charAt(0) == '+') && s.charAt(1) == '0' && (s.charAt(2) == 'x' || s.charAt(2) == 'X')) { // hex numbers with explicit signs vary between browsers. return null; } // FireFox and IE treat the "Infinity" differently. FireFox is case // insensitive, but IE treats "infinity" as NaN. So leave it alone. if (s.equals("infinity") || s.equals("-infinity") || s.equals("+infinity")) { return null; } try { return Double.parseDouble(s); } catch (NumberFormatException e) { return Double.NaN; } } static String trimJsWhiteSpace(String s) { int start = 0; int end = s.length(); while (end > 0 && isStrWhiteSpaceChar(s.charAt(end - 1)) == TernaryValue.TRUE) { end--; } while (start < end && isStrWhiteSpaceChar(s.charAt(start)) == TernaryValue.TRUE) { start++; } return s.substring(start, end); } /** * Copied from Rhino's ScriptRuntime */ static TernaryValue isStrWhiteSpaceChar(int c) { switch (c) { case '\u000B': // <VT> return TernaryValue.UNKNOWN; // IE says "no", EcmaScript says "yes" case ' ': // <SP> case '\n': // <LF> case '\r': // <CR> case '\t': // <TAB> case '\u00A0': // <NBSP> case '\u000C': // <FF> case '\u2028': // <LS> case '\u2029': // <PS> case '\uFEFF': // <BOM> return TernaryValue.TRUE; default: return (Character.getType(c) == Character.SPACE_SEPARATOR) ? TernaryValue.TRUE : TernaryValue.FALSE; } } /** * Gets the function's name. This method recognizes five forms: * <ul> *

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS> <li>{@code function name() ...}</li> * <li>{@code var name = function() ...}</li> * <li>{@code qualified.name = function() ...}</li> * <li>{@code var name2 = function name1() ...}</li> * <li>{@code qualified.name2 = function name1() ...}</li> * </ul> * In two last cases with named function expressions, the second name is * returned (the variable of qualified name). * * @param n a node whose type is {@link Token#FUNCTION} * @return the function's name, or {@code null} if it has no name */ static String getFunctionName(Node n) { Node parent = n.getParent(); String name = n.getFirstChild().getString(); switch (parent.getType()) { case Token.NAME: // var name = function() ... // var name2 = function name1() ... return parent.getString(); case Token.ASSIGN: // qualified.name = function() ... // qualified.name2 = function name1() ... return parent.getFirstChild().getQualifiedName(); default: // function name() ... return name != null && name.length() != 0 ? name : null; } } /** * Gets the function's name. This method recognizes the forms: * <ul> * <li>{@code &#123;'name': function() ...&#125;}</li> * <li>{@code &#123;name: function() ...&#125;}</li> * <li>{@code function name() ...}</li> * <li>{@code var name = function() ...}</li> * <li>{@code qualified.name = function() ...}</li> * <li>{@code var name2 = function name1() ...}</li> * <li>{@code qualified.name2 = function name1() ...}</li> * </ul> * * @param n a node whose type is {@link Token#FUNCTION} * @return the function's name, or {@code null} if it has no name */ static String getNearestFunctionName(Node n) { String name = getFunctionName(n); if (name != null) { return

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS> name; } // Check for the form { 'x' : function() { } } Node parent = n.getParent(); switch (parent.getType()) { case Token.SET: case Token.GET: case Token.STRING: // Return the name of the literal's key. return parent.getString(); case Token.NUMBER: return getStringValue(parent); } return null; } /** * Returns true if this is an immutable value. */ static boolean isImmutableValue(Node n) { switch (n.getType()) { case Token.STRING: case Token.NUMBER: case Token.NULL: case Token.TRUE: case Token.FALSE: return true; case Token.NOT: return isImmutableValue(n.getFirstChild()); case Token.VOID: case Token.NEG: return isImmutableValue(n.getFirstChild()); case Token.NAME: String name = n.getString(); // We assume here that programs don't change the value of the keyword // undefined to something other than the value undefined. return "undefined".equals(name) || "Infinity".equals(name) || "NaN".equals(name); } return false; } /** * Returns true if this is a literal value. We define a literal value * as any node that evaluates to the same thing regardless of when or * where it is evaluated. So /xyz/ and [3, 5] are literals, but * the name a is not. * * Function literals do not meet this definition, because they * lexically capture variables. For example, if you have * <code> * function() { return a; } * </code> * If it is evaluated in a different scope, then it * captures a different variable. Even if the function did not read * any captured vairables directly, it would still fail this definition, * because it affects the lifecycle of variables in the enclosing scope. * * However, a function literal with respect to a particular scope is * a literal. * * @param includeFunctions If true, all function expressions will be * treated as literals. */ static boolean isLiteralValue(Node n, boolean includeFunctions) { switch (n.getType

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS>.hasFinally(tryNode)); node.detachFromParent(); } else if (isTryCatchNodeContainer(node)) { // The container node itself can't be removed, but the contained CATCH // can if there is a 'finally' clause Node tryNode = node.getParent(); Preconditions.checkState(NodeUtil.hasFinally(tryNode)); node.detachChildren(); } else if (node.getType() == Token.BLOCK) { // Simply empty the block. This maintains source location and // "synthetic"-ness. node.detachChildren(); } else if (isStatementBlock(parent) || isSwitchCase(node)) { // A statement in a block can simply be removed. parent.removeChild(node); } else if (parent.getType() == Token.VAR) { if (parent.hasMoreThanOneChild()) { parent.removeChild(node); } else { // Remove the node from the parent, so it can be reused. parent.removeChild(node); // This would leave an empty VAR, remove the VAR itself. removeChild(parent.getParent(), parent); } } else if (parent.getType() == Token.LABEL && node == parent.getLastChild()) { // Remove the node from the parent, so it can be reused. parent.removeChild(node); // A LABEL without children can not be referred to, remove it. removeChild(parent.getParent(), parent); } else if (parent.getType() == Token.FOR && parent.getChildCount() == 4) { // Only Token.FOR can have an Token.EMPTY other control structure // need something for the condition. Others need to be replaced // or the structure removed. parent.replaceChild(node, new Node(Token.EMPTY)); } else { throw new IllegalStateException("Invalid attempt to remove node: " + node.toString() + " of "+ parent.toString()); } } /** * Add a finally block if one does not exist. */ static void maybeAddFinally(Node tryNode) { Preconditions.checkState(tryNode.getType() == Token.TRY); if (!NodeUtil.hasFinally(tryNode)) { tryNode.addChildrenToBack(new Node(Token.BLOCK) .copyInformationFrom(

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS> @param parent Parent of the node * @return True if n is the left hand of an assign */ static boolean isLhs(Node n, Node parent) { return (parent.getType() == Token.ASSIGN && parent.getFirstChild() == n) || parent.getType() == Token.VAR; } /** * Determines whether a node represents an object literal key * (e.g. key1 in {key1: value1, key2: value2}). * * @param node A node * @param parent The node's parent */ static boolean isObjectLitKey(Node node, Node parent) { switch (node.getType()) { case Token.NUMBER: case Token.STRING: return parent.getType() == Token.OBJECTLIT; case Token.GET: case Token.SET: return true; } return false; } /** * Get the name of an object literal key. * * @param key A node */ static String getObjectLitKeyName(Node key) { switch (key.getType()) { case Token.NUMBER: return NodeUtil.getStringValue(key); case Token.STRING: case Token.GET: case Token.SET: return key.getString(); } throw new IllegalStateException("Unexpected node type: " + key); } /** * @param key A OBJECTLIT key node. * @return The type expected when using the key. */ static JSType getObjectLitKeyTypeFromValueType(Node key, JSType valueType) { if (valueType != null) { switch (key.getType()) { case Token.GET: // GET must always return a function type. if (valueType.isFunctionType()) { FunctionType fntype = ((FunctionType) valueType); valueType = fntype.getReturnType(); } else { return null; } break; case Token.SET: if (valueType.isFunctionType()) { // SET must always return a function type. FunctionType fntype = ((FunctionType) valueType); Node param = fntype.getParametersNode().getFirstChild(); // SET function must always have one parameter. valueType = param.getJSType(); } else { return null; } break; }

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS>return The node created. */ static Node newName( CodingConvention convention, String name, Node basisNode, String originalName) { Node nameNode = newName(convention, name, basisNode); nameNode.putProp(Node.ORIGINALNAME_PROP, originalName); return nameNode; } /** Test if all characters in the string are in the Basic Latin (aka ASCII) * character set - that they have UTF-16 values equal to or below 0x7f. * This check can find which identifiers with Unicode characters need to be * escaped in order to allow resulting files to be processed by non-Unicode * aware UNIX tools and editors. * * * See http://en.wikipedia.org/wiki/Latin_characters_in_Unicode * for more on Basic Latin. * * @param s The string to be checked for ASCII-goodness. * * @return True if all characters in the string are in Basic Latin set. */ static boolean isLatin(String s) { char LARGEST_BASIC_LATIN = 0x7f; int len = s.length(); for (int index = 0; index < len; index++) { char c = s.charAt(index); if (c > LARGEST_BASIC_LATIN) { return false; } } return true; } /** * Determines whether the given name can appear on the right side of * the dot operator. Many properties (like reserved words) cannot. */ static boolean isValidPropertyName(String name) { return TokenStream.isJSIdentifier(name) && !TokenStream.isKeyword(name) && // no Unicode escaped characters - some browsers are less tolerant // of Unicode characters that might be valid according to the // language spec. // Note that by this point, unicode escapes have been converted // to UTF-16 characters, so we're only searching for character // values, not escapes. isLatin(name); } private static class VarCollector implements Visitor { final Map<String, Node> vars = Maps.newLinkedHashMap(); public void visit(Node n) { if (n.getType() == Token.NAME) { Node parent = n.getParent();

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS> if (parent != null && parent.getType() == Token.VAR) { String name = n.getString(); if (!vars.containsKey(name)) { vars.put(name, n); } } } } } /** * Retrieves vars declared in the current node tree, excluding descent scopes. */ public static Collection<Node> getVarsDeclaredInBranch(Node root) { VarCollector collector = new VarCollector(); visitPreOrder( root, collector, new MatchNotFunction()); return collector.vars.values(); } /** * @return {@code true} if the node an assignment to a prototype property of * some constructor. */ static boolean isPrototypePropertyDeclaration(Node n) { if (!isExprAssign(n)) { return false; } return isPrototypeProperty(n.getFirstChild().getFirstChild()); } static boolean isPrototypeProperty(Node n) { String lhsString = n.getQualifiedName(); if (lhsString == null) { return false; } int prototypeIdx = lhsString.indexOf(".prototype."); return prototypeIdx != -1; } /** * @return The class name part of a qualified prototype name. */ static Node getPrototypeClassName(Node qName) { Node cur = qName; while (isGetProp(cur)) { if (cur.getLastChild().getString().equals("prototype")) { return cur.getFirstChild(); } else { cur = cur.getFirstChild(); } } return null; } /** * @return The string property name part of a qualified prototype name. */ static String getPrototypePropertyName(Node qName) { String qNameStr = qName.getQualifiedName(); int prototypeIdx = qNameStr.lastIndexOf(".prototype."); int memberIndex = prototypeIdx + ".prototype".length() + 1; return qNameStr.substring(memberIndex); } /** * Create a node for an empty result expression: * "void 0" */ static Node newUndefinedNode(Node srcReferenceNode) { Node node = new Node(Token.VOID, Node.newNumber(0)); if (srcReferenceNode != null) { node.copyInformationFromForTree(srcReference

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS>Node); } return node; } /** * Create a VAR node containing the given name and initial value expression. */ static Node newVarNode(String name, Node value) { Node nodeName = Node.newString(Token.NAME, name); if (value != null) { Preconditions.checkState(value.getNext() == null); nodeName.addChildToBack(value); nodeName.copyInformationFrom(value); } Node var = new Node(Token.VAR, nodeName) .copyInformationFrom(nodeName); return var; } /** * A predicate for matching name nodes with the specified node. */ private static class MatchNameNode implements Predicate<Node>{ final String name; MatchNameNode(String name){ this.name = name; } public boolean apply(Node n) { return n.getType() == Token.NAME && n.getString().equals(name); } } /** * A predicate for matching nodes with the specified type. */ static class MatchNodeType implements Predicate<Node>{ final int type; MatchNodeType(int type){ this.type = type; } public boolean apply(Node n) { return n.getType() == type; } } /** * A predicate for matching var or function declarations. */ static class MatchDeclaration implements Predicate<Node> { public boolean apply(Node n) { return isFunctionDeclaration(n) || n.getType() == Token.VAR; } } /** * A predicate for matching anything except function nodes. */ static class MatchNotFunction implements Predicate<Node>{ public boolean apply(Node n) { return !isFunction(n); } } /** * A predicate for matching statements without exiting the current scope. */ static class MatchShallowStatement implements Predicate<Node>{ public boolean apply(Node n) { Node parent = n.getParent(); return n.getType() == Token.BLOCK || (!isFunction(n) && (parent == null || isControlStructure(parent) || isStatementBlock(parent))); } } /** * Finds the number of times a type is referenced within the node tree. */ static int getNodeTypeReferenceCount( Node node, int type, Predicate<Node>

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS> * <p>Determining whether a variable is constant has three steps: * <ol> * <li>In CodingConventionAnnotator, any name that matches the * {@link CodingConvention#isConstant(String)} is annotated with an * IS_CONSTANT_NAME property. * <li>The normalize pass renames any variable with the IS_CONSTANT_NAME * annotation and that is initialized to a constant value with * a variable name inlucding $$constant. * <li>Return true here if the variable includes $$constant in its name. * </ol> * * @param node A NAME or STRING node * @return True if the variable is constant */ static boolean isConstantName(Node node) { return node.getBooleanProp(Node.IS_CONSTANT_NAME); } /** Whether the given name is constant by coding convention. */ static boolean isConstantByConvention( CodingConvention convention, Node node, Node parent) { String name = node.getString(); if (parent.getType() == Token.GETPROP && node == parent.getLastChild()) { return convention.isConstantKey(name); } else if (isObjectLitKey(node, parent)) { return convention.isConstantKey(name); } else { return convention.isConstant(name); } } /** * @param nameNode A name node * @return The JSDocInfo for the name node */ static JSDocInfo getInfoForNameNode(Node nameNode) { JSDocInfo info = null; Node parent = null; if (nameNode != null) { info = nameNode.getJSDocInfo(); parent = nameNode.getParent(); } if (info == null && parent != null && ((parent.getType() == Token.VAR && parent.hasOneChild()) || parent.getType() == Token.FUNCTION)) { info = parent.getJSDocInfo(); } return info; } /** * Get the JSDocInfo for a function. */ static JSDocInfo getFunctionInfo(Node n) { Preconditions.checkState(n.getType() == Token.FUNCTION); JSDocInfo fnInfo = n.getJSDocInfo(); if (fn

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS>Info == null && NodeUtil.isFunctionExpression(n)) { // Look for the info on other nodes. Node parent = n.getParent(); if (parent.getType() == Token.ASSIGN) { // on ASSIGNs fnInfo = parent.getJSDocInfo(); } else if (parent.getType() == Token.NAME) { // on var NAME = function() { ... }; fnInfo = parent.getParent().getJSDocInfo(); } } return fnInfo; } /** * @param n The node. * @return The source name property on the node or its ancestors. */ static String getSourceName(Node n) { String sourceName = null; while (sourceName == null && n != null) { sourceName = (String) n.getProp(Node.SOURCENAME_PROP); n = n.getParent(); } return sourceName; } /** * A new CALL node with the "FREE_CALL" set based on call target. */ static Node newCallNode(Node callTarget, Node... parameters) { boolean isFreeCall = isName(callTarget); Node call = new Node(Token.CALL, callTarget); call.putBooleanProp(Node.FREE_CALL, isFreeCall); for (Node parameter : parameters) { call.addChildToBack(parameter); } return call; } /** * @return Whether the node is known to be a value that is not referenced * elsewhere. */ static boolean evaluatesToLocalValue(Node value) { return evaluatesToLocalValue(value, Predicates.<Node>alwaysFalse()); } /** * @param locals A predicate to apply to unknown local values. * @return Whether the node is known to be a value that is not a reference * outside the expression scope. */ static boolean evaluatesToLocalValue(Node value, Predicate<Node> locals) { switch (value.getType()) { case Token.ASSIGN: // A result that is aliased by a non-local name, is the effectively the // same as returning a non-local name, but this doesn't matter if the // value is immutable. return NodeUtil.isImmutableValue(value.getLastChild()) || (locals.apply

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS>(value) && evaluatesToLocalValue(value.getLastChild(), locals)); case Token.COMMA: return evaluatesToLocalValue(value.getLastChild(), locals); case Token.AND: case Token.OR: return evaluatesToLocalValue(value.getFirstChild(), locals) && evaluatesToLocalValue(value.getLastChild(), locals); case Token.HOOK: return evaluatesToLocalValue(value.getFirstChild().getNext(), locals) && evaluatesToLocalValue(value.getLastChild(), locals); case Token.INC: case Token.DEC: if (value.getBooleanProp(Node.INCRDECR_PROP)) { return evaluatesToLocalValue(value.getFirstChild(), locals); } else { return true; } case Token.THIS: return locals.apply(value); case Token.NAME: return isImmutableValue(value) || locals.apply(value); case Token.GETELEM: case Token.GETPROP: // There is no information about the locality of object properties. return locals.apply(value); case Token.CALL: return callHasLocalResult(value) || isToStringMethodCall(value) || locals.apply(value); case Token.NEW: return newHasLocalResult(value) || locals.apply(value); case Token.FUNCTION: case Token.REGEXP: case Token.ARRAYLIT: case Token.OBJECTLIT: // Literals objects with non-literal children are allowed. return true; case Token.DELPROP: case Token.IN: // TODO(johnlenz): should IN operator be included in #isSimpleOperator? return true; default: // Other op force a local value: // x = '' + g (x is now an local string) // x -= g (x is now an local number) if (isAssignmentOp(value) || isSimpleOperator(value) || isImmutableValue(value)) { return true; } throw new IllegalStateException( "Unexpected expression node" + value + "\n parent:" + value.getParent()); } } /** * Given the first sibling, this returns the nth * sibling or null if no such sibling exists. * This is like "getChildAtIndex" but returns null for non-existent indexes.

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS> */ private static Node getNthSibling(Node first, int index) { Node sibling = first; while (index != 0 && sibling != null) { sibling = sibling.getNext(); index--; } return sibling; } /** * Given the function, this returns the nth * argument or null if no such parameter exists. */ static Node getArgumentForFunction(Node function, int index) { Preconditions.checkState(isFunction(function)); return getNthSibling( function.getFirstChild().getNext().getFirstChild(), index); } /** * Given the new or call, this returns the nth * argument of the call or null if no such argument exists. */ static Node getArgumentForCallOrNew(Node call, int index) { Preconditions.checkState(isCallOrNew(call)); return getNthSibling( call.getFirstChild().getNext(), index); } private static boolean isToStringMethodCall(Node call) { Node getNode = call.getFirstChild(); if (isGet(getNode)) { Node propNode = getNode.getLastChild(); return isString(propNode) && "toString".equals(propNode.getString()); } return false; } }

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS> keeps logging events and never clears them). * This number is arbitrary and can be increased if necessary (though * if there are more than 1000 events then the Tracer is probably being * misused). */ static final int MAX_TRACE_SIZE = 1000; /** * For unit testing. Can't use {@link com.google.common.time.Clock} because * this code is in base and has minimal dependencies. */ static interface InternalClock { long currentTimeMillis(); } /** * Default clock that calls through to the system clock. Can be overridden * in unit tests. */ static InternalClock clock = new InternalClock() { public long currentTimeMillis() { return System.currentTimeMillis(); } }; /** * Create and start a tracer. * Both type and comment may be null. See class comment for usage. * * @param type The type for totalling * @param comment Comment about this tracer */ Tracer(@Nullable String type, @Nullable String comment) { this.type = type; this.comment = comment == null ? "" : comment; startTimeMs = clock.currentTimeMillis(); startThread = Thread.currentThread(); if (!extraTracingStatistics.isEmpty()) { int size = extraTracingStatistics.size(); extraTracingValues = new long[size]; int i = 0; for (TracingStatistic tracingStatistic : extraTracingStatistics) { extraTracingValues[i] = tracingStatistic.start(startThread); i++; } } ThreadTrace trace = getThreadTrace(); // Do nothing if the current thread trace wasn't initialized. if (!trace.isInitialized()) { return; } // Check if we are creating too many Tracers. if (trace.events.size() >= MAX_TRACE_SIZE) { logger.log(Level.WARNING, "Giant thread trace. Too many Tracers created. " + "Clearing to avoid memory leak.", new Throwable(trace.toString())); trace.truncateEvents(); } // Check if we forgot to close the Tracers. if (trace.outstandingEvents.size() >= MAX_TRACE_SIZE) { logger.log(Level.WARNING, "Too many outstanding Tracers. Tracer

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS>.stop() is missing " + "or Tracer.stop() is not wrapped in a " + "try/finally block. " + "Clearing to avoid memory leak.", new Throwable(trace.toString())); trace.truncateOutstandingEvents(); } trace.startEvent(this); } /** * Create a tracer that isn't summed as part of a total * * @param comment Comment about this tracer */ Tracer(String comment) { this(null, comment); } /** * Construct a tracer whose type is based on the short name of the object * @param object Object to use as type name * @param comment A comment * @return new Tracer. */ static Tracer shortName(Object object, String comment) { if (object == null) { return new Tracer(comment); } return new Tracer(object.getClass().getSimpleName(), comment); } /** * Converts 'v' to a string and pads it with up to 16 spaces for * improved alignment. * @param v The value to convert. * @param digits_column_width The desired with of the string. */ private static String longToPaddedString(long v, int digits_column_width) { int digit_width = numDigits(v); StringBuilder sb = new StringBuilder(); appendSpaces(sb, digits_column_width - digit_width); sb.append(v); return sb.toString(); } /** * Gets the number of digits in an integer when printed in base 10. Assumes * a positive integer. * @param n The value. * @return The number of digits in the string. */ private static int numDigits(long n) { int i = 0; do { i++; n = n / 10; } while (n > 0); return i; } /** * Gets a string of spaces of the length specified. * @param sb The string builder to append to. * @param numSpaces The number of spaces in the string. */ @VisibleForTesting static void appendSpaces(StringBuilder sb, int numSpaces) { if (numSpaces > 16) {

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS> silence_threshold) { Preconditions.checkState(Thread.currentThread() == startThread); ThreadTrace trace = getThreadTrace(); // Do nothing if the thread trace was not initialized. if (!trace.isInitialized()) { return 0; } stopTimeMs = clock.currentTimeMillis(); if (extraTracingValues != null) { // We use extraTracingValues.length rather than // extraTracingStatistics.size() because a new statistic may // have been added for (int i = 0; i < extraTracingValues.length; i++) { long value = extraTracingStatistics.get(i).stop(startThread); extraTracingValues[i] = value - extraTracingValues[i]; } } // Do nothing if the thread trace was not initialized. if (!trace.isInitialized()) { return 0; } trace.endEvent(this, silence_threshold); return stopTimeMs - startTimeMs; } /** Stop the trace using the default silence_threshold * * @return The time that this trace actually ran. */ long stop() { return stop(-1); } @Override public String toString() { if (type == null) { return comment; } else { return "[" + type + "] " + comment; } } static void setDefaultSilenceThreshold(int threshold) { getThreadTrace().defaultSilenceThreshold = threshold; } /** * Initialize the trace associated with the current thread by clearing * out any existing trace. There shouldn't be a trace so if one is * found we log it as an error. */ static void initCurrentThreadTrace() { ThreadTrace events = getThreadTrace(); if (!events.isEmpty()) { logger.log(Level.WARNING, "Non-empty timer log:\n" + events, new Throwable()); clearThreadTrace(); // Grab a new thread trace if we find a previous non-empty ThreadTrace. events = getThreadTrace(); } // Mark the thread trace as initialized. events.init(); } static void initCurrentThreadTrace(int default_silence_threshold) { initCurrentThreadTrace(); setDefaultSilenceThreshold(default_silence_threshold); } /** * Returns a timer report similar

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS> to the one described in the class comment. * * @return The timer report as a string */ static String getCurrentThreadTraceReport() { return getThreadTrace().toString(); } /** * Logs a timer report similar to the one described in the class comment. */ static void logCurrentThreadTrace() { ThreadTrace trace = getThreadTrace(); // New threads must call Tracer.initCurrentThreadTrace() before Tracer // statistics are gathered. This is a recent change (Jun 2007) that // prevents spurious Third Eye messages when an application uses a class in // a different package that happens to call Tracer without knowledge of the // application authors. if (!trace.isInitialized()) { logger.log(Level.WARNING, "Tracer log requested for this thread but was not " + "initialized using Tracer.initCurrentThreadTrace().", new Throwable()); return; } if (!trace.isEmpty()) { logger.log(Level.INFO, "timers:\n{0}", getCurrentThreadTraceReport()); } } /** * Throw away any Trace associated with the current thread. */ static void clearCurrentThreadTrace() { clearThreadTrace(); } /** * logCurrentThreadTrace() then clearCurrentThreadTrace() */ static void logAndClearCurrentThreadTrace() { logCurrentThreadTrace(); clearThreadTrace(); } /** * Sets whether pretty printing is enabled. See class-level comment. This * only affects tracers created after this is called. * @param enabled Whether to enable pretty printing. */ static void setPrettyPrint(boolean enabled) { defaultPrettyPrint = enabled; } /** Statistics for a given tracer type */ static final class Stat { private int count; private int silent; private int clockTime; private int[] extraInfo; /** total count of tracers of a type, including silent * * @return total count of tracers, including silent tracers */ int getCount() { return count; } /** total count of silent tracers of a type * * @return total count of silent tracers */ int getSilentCount() { return silent; } /** total time spent in tracers of a

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS> type, in ms * * @return total time spent in tracer, in ms */ int getTotalTime() { return clockTime; } /** total time spent doing additional things that we are clocking */ @VisibleForTesting int getExtraInfo(int index) { return index >= extraInfo.length ? 0 : extraInfo[index]; } } /** * This map tracks counts of tracers for each type over all time. */ private static @Nullable AtomicTracerStatMap typeToCountMap; /** * This map tracks counts of silent tracers for each type over all time. */ private static @Nullable AtomicTracerStatMap typeToSilentMap; /** * This map tracks time (ms) for each type over all time. */ private static @Nullable AtomicTracerStatMap typeToTimeMap; /** * This method MUST be called before getTypeToCountMap (and friends) * will return a valid map. This is because computing this information * imposes a synchronization penalty on every Tracer that is stopped. */ static synchronized void enableTypeMaps() { if (typeToCountMap == null) { typeToCountMap = new AtomicTracerStatMap(); typeToSilentMap = new AtomicTracerStatMap(); typeToTimeMap = new AtomicTracerStatMap(); } } /** * Used for exporting this data via varz. Accesses to this * map must be synchronized on the map. If enableTypeMaps has not * been called, this will return null. */ static @Nullable Map<String, Long> getTypeToCountMap() { return typeToCountMap != null ? typeToCountMap.getMap() : null; } /** * Used for exporting this data via varz. Accesses to this * map must be synchronized on the map. If enableTypeMaps has not * been called, this will return null. */ static @Nullable Map<String, Long> getTypeToSilentMap() { return typeToSilentMap != null ? typeToSilentMap.getMap() : null; } /** * Used for exporting this data via varz. Accesses to this * map must be synchronized on the map. If enableTypeMaps has not * been called,

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS> this will return null. */ static @Nullable Map<String, Long> getTypeToTimeMap() { return typeToTimeMap != null ? typeToTimeMap.getMap() : null; } /** Gets the Stat for a tracer type; never returns null */ static Stat getStatsForType(String type) { Stat stat = getThreadTrace().stats.get(type); return stat != null ? stat : ZERO_STAT; } private static final Stat ZERO_STAT = new Stat(); /** Return the sec.ms part of time (if time = "20:06:11.566", "11.566") */ private static String formatTime(long time) { int sec = (int) ((time / 1000) % 60); int ms = (int) (time % 1000); return String.format("%02d.%03d", sec, ms); } /** An event is created every time a Tracer is created or stopped */ private static final class Event { boolean isStart; // else is_stop Tracer tracer; Event(boolean start, Tracer t) { isStart = start; tracer = t; } long eventTime() { return isStart ? tracer.startTimeMs : tracer.stopTimeMs; } /** * Converts the event to a formatted string. * @param prevEventTime The time of the previous event which appears at * the left most part of the trace line. * @param indent The indentation to put before the tracer to show the * hieararchy. * @param digitsColWidth How many characters the digits should use. * @return The formatted string. */ String toString(long prevEventTime, String indent, int digitsColWidth) { StringBuilder sb = new StringBuilder(120); if (prevEventTime == -1) { appendSpaces(sb, digitsColWidth); } else { sb.append(longToPaddedString( eventTime() - prevEventTime, digitsColWidth)); } sb.append(' '); sb.append(formatTime(eventTime())); if (isStart) { sb.append(" Start "); appendSpaces(sb, digits

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS>ColWidth); sb.append(" "); } else { sb.append(" Done "); long delta = tracer.stopTimeMs - tracer.startTimeMs; sb.append(longToPaddedString(delta, digitsColWidth)); sb.append(" ms "); if (tracer.extraTracingValues != null) { for (int i = 0; i < tracer.extraTracingValues.length; i++) { delta = tracer.extraTracingValues[i]; sb.append(String.format("%4d", delta)); sb.append(extraTracingStatistics.get(i).getUnits()); sb.append("; "); } } } sb.append(indent); sb.append(tracer.toString()); return sb.toString(); } } /** Stores a thread's Trace */ static final class ThreadTrace { /** Events taking less than this number of milliseconds are not reported. */ int defaultSilenceThreshold; // non-final /** The Events corresponding to each startEvent/stopEvent */ final ArrayList<Event> events = new ArrayList<Event>(); /** Tracers that have not had their .stop() called */ final HashSet<Tracer> outstandingEvents = new HashSet<Tracer>(); /** Map from type to Stat object */ final Map<String, Stat> stats = new HashMap<String, Stat>(); /** * True if {@code outstandingEvents} has been cleared because we exceeded * the max trace limit. */ boolean isOutstandingEventsTruncated = false; /** * True if {@code events} has been cleared because we exceeded the max * trace limit. */ boolean isEventsTruncated = false; /** * Set to true if {@link Tracer#initCurrentThreadTrace()} was called by * the current thread. */ boolean isInitialized = false; /** * Whether pretty printing is enabled for the trace. */ boolean prettyPrint = false; /** Initialize the trace. */ void init() { isInitialized = true; } /** Is initialized? */ boolean isInitialized() { return isInitialized; } /** * Called by the constructor {@link Tracer#Tracer(String, String)} to create * a start event. */ void startEvent(Tracer t) { events.add(new Event(

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS>typeToTimeMap != null) { typeToTimeMap.incrementBy(t.type, elapsed); } if (stat.extraInfo != null && t.extraTracingValues != null) { int overlapLength = Math.min(stat.extraInfo.length, t.extraTracingValues.length); for (int i = 0; i < overlapLength; i++) { stat.extraInfo[i] += t.extraTracingValues[i]; AtomicTracerStatMap map = extraTracingStatistics.get(i).getTracingStat(); if (map != null) { map.incrementBy(t.type, t.extraTracingValues[i]); } } } if (elapsed < silenceThreshold) { stat.silent++; if (typeToSilentMap != null) { typeToSilentMap.incrementBy(t.type, 1); } } } } boolean isEmpty() { return events.size() == 0 && outstandingEvents.size() == 0; } void truncateOutstandingEvents() { isOutstandingEventsTruncated = true; outstandingEvents.clear(); } void truncateEvents() { isEventsTruncated = true; events.clear(); } /** Produces the lovely Trace seen in the class comments */ // Nullness checker does not understand that prettyPrint => indent != null @SuppressWarnings("nullness") @Override public String toString() { int numDigits = getMaxDigits(); StringBuilder sb = new StringBuilder(); long etime = -1; LinkedList<String> indent = prettyPrint ? new LinkedList<String>() : null; for (Event e : events) { if (prettyPrint && !e.isStart && !indent.isEmpty()) { indent.pop(); } sb.append(" "); if (prettyPrint) { sb.append(e.toString(etime, Joiner.on("").join(indent), numDigits)); } else { sb.append(e.toString(etime, "", 4)); } etime = e.eventTime(); sb.append('\n'); if (prettyPrint && e.isStart) { indent.push("| "); } } if (outstandingEvents.size() != 0) {

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS> long now = clock.currentTimeMillis(); sb.append(" Unstopped timers:\n"); for (Tracer t : outstandingEvents) { sb.append(" "). append(t). append(" ("). append(now - t.startTimeMs). append(" ms, started at "). append(formatTime(t.startTimeMs)). append(")\n"); } } for (String key : stats.keySet()) { Stat stat = stats.get(key); if (stat.count > 1) { sb.append(" TOTAL "). append(key). append(" "). append(stat.count). append(" ("). append(stat.clockTime). append(" ms"); if (stat.extraInfo != null) { for (int i = 0; i < stat.extraInfo.length; i++) { sb.append("; "); sb.append(stat.extraInfo[i]). append(' '). append(extraTracingStatistics.get(i).getUnits()); } } sb.append(")\n"); } } return sb.toString(); } /** * Gets the maximum number of digits that can appear in the tracer output * in the gaps between tracers or the duration of a tracer. This is used * by the pretty printing case so that all of the tracers are aligned. */ private int getMaxDigits() { long etime = -1; long max_time = 0; for (Event e : events) { if (etime != -1) { long time = e.eventTime() - etime; max_time = Math.max(max_time, time); } if (!e.isStart) { long time = e.tracer.stopTimeMs - e.tracer.startTimeMs; max_time = Math.max(max_time, time); } etime = e.eventTime(); } // Minimum is 3 to preserve an indent even when max is small. return Math.max(3, numDigits(max_time)); } } /** Holds the ThreadTrace for each thread. */ private static ThreadLocal<ThreadTrace> traces = new ThreadLocal<ThreadTrace>(); /** * Get the

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS> class AtomicTracerStatMap { private ConcurrentMap<String, Long> map = new ConcurrentHashMap<String, Long>(); /** * Atomically increment the specified field by the specified amount. * * @param key the name of the field * @param delta the amount by which to increment the field */ // Nullness checker is not powerful enough to prove null-safety of // this method @SuppressWarnings("nullness") void incrementBy(String key, long delta) { // We use a compareAndSet strategy to update the map, which is much // faster when there isn't too much contention. Look at a value, and // conditionally update the map if the value hasn't changed. // If it has changed, repeat. Long oldValue = map.get(key); if (oldValue == null) { // Currently, the slot is empty oldValue = map.putIfAbsent(key, delta); if (oldValue == null) { // The slot was still empty when we set it return; } else { // Someone filled in the slot behind our back. oldValue has // its current value } } while (true) { if (map.replace(key, oldValue, oldValue + delta)) { break; } // Nullness checker doesn't understand that this cannot return null. oldValue = map.get(key); } } /** * Returns a map of key:value pairs. */ Map<String, Long> getMap() { return map; } } }

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS> a subtype of typeB */ static boolean isSubtype(ObjectType typeA, RecordType typeB) { // typeA is a subtype of record type typeB iff: // 1) typeA has all the properties declared in typeB. // 2) And for each property of typeB, // 2a) if the property of typeA is declared, it must be equal // to the type of the property of typeB, // 2b) otherwise, it must be a subtype of the property of typeB. // // To figure out why this is true, consider the following pseudo-code: // /** @type {{a: (Object,null)}} */ var x; // /** @type {{a: !Object}} */ var y; // var z = {a: {}}; // x.a = null; // // y cannot be assigned to x, because line 4 would violate y's declared // properties. But z can be assigned to x. Even though z and y are the // same type, the properties of z are inferred--and so an assignment // to the property of z would not violate any restrictions on it. for (String property : typeB.properties.keySet()) { if (!typeA.hasProperty(property)) { return false; } JSType propA = typeA.getPropertyType(property); JSType propB = typeB.getPropertyType(property); if (!propA.isUnknownType() && !propB.isUnknownType()) { if (typeA.isPropertyTypeDeclared(property)) { if (!propA.isEquivalentTo(propB)) { return false; } } else { if (!propA.isSubtype(propB)) { return false; } } } } return true; } @Override public String toString() { StringBuilder sb = new StringBuilder(); sb.append("{ "); int i = 0; for (String property : properties.keySet()) { if (i > 0) { sb.append(", "); } sb.append(property); sb.append(" : "); sb.append(properties.get(property).toString()); ++i; } sb.append(" }"); return sb.toString();

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS>see NodeTraversal * @see DataFlowAnalysis * */ public class Scope implements StaticScope<JSType> { private final Map<String, Var> vars = new LinkedHashMap<String, Var>(); private final Scope parent; private final int depth; private final Node rootNode; /** The type of {@code this} in the current scope. */ private final ObjectType thisType; /** Whether this is a bottom scope for the purposes of type inference. */ private final boolean isBottom; private Var arguments; /** Stores info about a variable */ public static class Var implements StaticSlot<JSType> { /** name */ final String name; /** Var node */ final Node nameNode; /** * The variable's type. */ private JSType type; /** * The variable's doc info. */ private final JSDocInfo info; /** * Whether the variable's type has been inferred or is declared. An inferred * type may change over time (as more code is discovered), whereas a * declared type is a static contract that must be matched. */ private final boolean typeInferred; /** Input source */ final CompilerInput input; /** Whether the variable is a define */ final boolean isDefine; /** * The index at which the var is declared. e..g if it's 0, it's the first * declared variable in that scope */ final int index; /** The enclosing scope */ final Scope scope; /** * Creates a variable. * * @param inferred whether its type is inferred (as opposed to declared) */ private Var(boolean inferred, String name, Node nameNode, JSType type, Scope scope, int index, CompilerInput input, boolean isDefine, JSDocInfo info) { this.name = name; this.nameNode = nameNode; this.type = type; this.scope = scope; this.index = index; this.input = input; this.isDefine = isDefine; this.info = info; this.typeInferred = inferred; } /** * Gets the name of the variable. */ public String getName() { return name; } /** * Gets the parent of the name node. */ public Node

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS> getParentNode() { return nameNode == null ? null : nameNode.getParent(); } /** * Whether this is a bleeding function (an anonymous named function * that bleeds into the inner scope. */ public boolean isBleedingFunction() { return NodeUtil.isFunctionExpression(getParentNode()); } /** * Gets the scope where this variable is declared. */ Scope getScope() { return scope; } /** * Returns whether this is a global variable. */ public boolean isGlobal() { return scope.isGlobal(); } /** * Returns whether this is a local variable. */ public boolean isLocal() { return scope.isLocal(); } /** * Returns whether this is defined in an extern file. */ boolean isExtern() { return input == null || input.isExtern(); } /** * Returns {@code true} if the variable is declared as a constant, * based on the value reported by {@code NodeUtil}. */ public boolean isConst() { return nameNode != null && NodeUtil.isConstantName(nameNode); } /** * Returns {@code true} if the variable is declared as a define. * A variable is a define if it is annotaed by {@code @define}. */ public boolean isDefine() { return isDefine; } public Node getInitialValue() { Node parent = getParentNode(); int pType = parent.getType(); if (pType == Token.FUNCTION) { return parent; } else if (pType == Token.ASSIGN) { return parent.getLastChild(); } else if (pType == Token.VAR) { return nameNode.getFirstChild(); } else { return null; } } /** * Gets this variable's type. To know whether this type has been inferred, * see {@code #isInferred()}. */ public JSType getType() { return type; } /** * Returns the name node that produced this variable. */ public Node getNameNode() { return nameNode; } /** * Gets the JSDocInfo for the variable. */ public JSDocInfo getJSDocInfo() { return info; } /**

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS> * Sets this variable's type. * @throws IllegalStateException if the variable's type is not inferred */ void setType(JSType type) { Preconditions.checkState(isTypeInferred()); this.type = type; } /** * Resolve this variable's type. */ void resolveType(ErrorReporter errorReporter) { if (type != null) { type = type.resolve(errorReporter, scope); } } /** * Returns whether this variable's type is inferred. To get the variable's * type, see {@link #getType()}. */ public boolean isTypeInferred() { return typeInferred; } public String getInputName() { if (input == null) return "<non-file>"; else return input.getName(); } public boolean isNoShadow() { if (info != null && info.isNoShadow()) { return true; } else { return false; } } @Override public boolean equals(Object other) { if (!(other instanceof Var)) { return false; } Var otherVar = (Var) other; return otherVar.nameNode == nameNode; } @Override public int hashCode() { return nameNode.hashCode(); } @Override public String toString() { return "Scope.Var " + name; } } /** * A special subclass of Var used to distinguish "arguments" in the current * scope. */ // TODO(johnlenz): Include this the list of Vars for the scope. public static class Arguments extends Var { Arguments(Scope scope) { super( false, // no inferred "arguments", // always arguments null, // no declaration node // TODO(johnlenz): provide the type of "Arguments". null, // no type info scope, -1, // no variable index null, // input, false, // not a define null // no jsdoc ); } @Override public boolean equals(Object other) { if (!(other instanceof Arguments)) { return false; } Arguments otherVar = (Arguments) other; return otherVar.scope.getRootNode() == scope.getRootNode(); } @Override public int hashCode() {

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS> { result = result.getParent(); } return result; } @Override public StaticScope<JSType> getParentScope() { return parent; } /** * Gets the type of {@code this} in the current scope. */ public ObjectType getTypeOfThis() { return thisType; } /** * Declares a variable whose type is inferred. * * @param name name of the variable * @param nameNode the NAME node declaring the variable * @param type the variable's type * @param input the input in which this variable is defined. */ Var declare(String name, Node nameNode, JSType type, CompilerInput input) { return declare(name, nameNode, type, input, true); } /** * Declares a variable. * * @param name name of the variable * @param nameNode the NAME node declaring the variable * @param type the variable's type * @param input the input in which this variable is defined. * @param inferred Whether this variable's type is inferred (as opposed * to declared). */ Var declare(String name, Node nameNode, JSType type, CompilerInput input, boolean inferred) { Preconditions.checkState(name != null && name.length() > 0); // Make sure that it's declared only once Preconditions.checkState(vars.get(name) == null); // native variables do not have a name node. // TODO(user): make Var abstract and have NativeVar, NormalVar. JSDocInfo info = NodeUtil.getInfoForNameNode(nameNode); Var var = new Var(inferred, name, nameNode, type, this, vars.size(), input, info != null && info.isDefine(), info); vars.put(name, var); return var; } /** * Undeclares a variable, to be used when the compiler optimizes out * a variable and removes it from the scope. */ void undeclare(Var var) { Preconditions.checkState(var.scope == this); Preconditions.checkState(vars.get(var.name) == var); vars.remove(var.name); } public StaticSlot<JSType> getSlot(String name)

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS>/* * Copyright 2008 The Closure Compiler Authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.google.javascript.jscomp.graph; import java.util.Collection; /** * A minimal graph interface. Provided is add nodes to the graph, adjacency * calculation between a SubGraph and a GraphNode, and adding node annotations. * * <p>For a more extensive interface, see {@link Graph}. * * * @param <N> Value type that the graph node stores. * @param <E> Value type that the graph edge stores. * @see Graph */ public interface AdjacencyGraph<N, E> { /** Gets an immutable list of all nodes. */ Collection<GraphNode<N, E>> getNodes(); /** * Gets a node from the graph given a value. Values equality are compared * using <code>Object.equals</code>. * * @param value The node's value. * @return The corresponding node in the graph, null if there value has no * corresponding node. */ GraphNode<N, E> getNode(N value); /** Returns an empty SubGraph for this Graph. */ SubGraph<N, E> newSubGraph(); /** Makes each node's annotation null. */ void clearNodeAnnotations(); /** * Returns a weight for the given value to be used in ordering nodes, e.g. * in {@link GraphColoring}. */ int getWeight(N value); }

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS> All type is the greatest type (top) and is never a subtype of * another except itself or the Unknown type or a named alias. * @return {@code this.isEquivalentTo(that)} */ @Override public boolean isSubtype(JSType that) { return JSType.isSubtype(this, that); } @Override public boolean isAllType() { return true; } @Override public boolean matchesStringContext() { // Be lenient. return true; } @Override public boolean matchesObjectContext() { // Be lenient. return true; } @Override public boolean canBeCalled() { return false; } @Override public TernaryValue testForEquality(JSType that) { return UNKNOWN; } @Override public String toString() { return "*"; } @Override public String getDisplayName() { return "<Any Type>"; } @Override public boolean hasDisplayName() { return true; } @Override public <T> T visit(Visitor<T> visitor) { return visitor.caseAllType(); } @Override public BooleanLiteralSet getPossibleToBooleanOutcomes() { return BooleanLiteralSet.BOTH; } @Override JSType resolveInternal(ErrorReporter t, StaticScope<JSType> scope) { return this; } }

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS> of tweaks based on compiler options. */ private void applyCompilerDefaultValueOverrides( Map<String, TweakInfo> tweakInfos) { for (Entry<String, Node> entry : compilerDefaultValueOverrides.entrySet()) { String tweakId = entry.getKey(); TweakInfo tweakInfo = tweakInfos.get(tweakId); if (tweakInfo == null) { compiler.report(JSError.make(UNKNOWN_TWEAK_WARNING, tweakId)); } else { TweakFunction registerFunc = tweakInfo.registerCall.tweakFunc; Node value = entry.getValue(); if (!registerFunc.isValidNodeType(value.getType())) { compiler.report(JSError.make(INVALID_TWEAK_DEFAULT_VALUE_WARNING, tweakId, registerFunc.getName(), registerFunc.getExpectedTypeName())); } else { tweakInfo.defaultValueNode = value; } } } } /** * Finds all calls to goog.tweak functions and emits warnings/errors if any * of the calls have issues. * @return A map of {@link TweakInfo} structures, keyed by tweak ID. */ private CollectTweaksResult collectTweaks(Node root) { CollectTweaks pass = new CollectTweaks(); NodeTraversal.traverse(compiler, root, pass); Map<String, TweakInfo> tweakInfos = pass.allTweaks; for (TweakInfo tweakInfo: tweakInfos.values()) { tweakInfo.emitAllWarnings(); } return new CollectTweaksResult(tweakInfos, pass.getOverridesCalls); } private final static class CollectTweaksResult { final Map<String, TweakInfo> tweakInfos; final List<TweakFunctionCall> getOverridesCalls; CollectTweaksResult(Map<String, TweakInfo> tweakInfos, List<TweakFunctionCall> getOverridesCalls) { this.tweakInfos = tweakInfos; this.getOverridesCalls = getOverridesCalls; } } /** * Processes all calls to goog.tweak functions. */ private final class CollectTweaks extends AbstractPostOrderCallback { final Map<String, TweakInfo> allTweaks = Maps.newHashMap(); final List<TweakFunctionCall

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS> number, and date. */ public final String getImplementationVersion() { // XXX Probably it would be better to embed this directly into source // with special build preprocessing but that would require some ant // tweaking and then replacing token in resource files was simpler if (implementationVersion == null) { implementationVersion = ScriptRuntime.getMessage0("implementation.version"); } return implementationVersion; } /** * Get the current error reporter. * * @see com.google.javascript.rhino.ErrorReporter */ public final ErrorReporter getErrorReporter() { return errorReporter; } /** * Change the current error reporter. * * @return the previous error reporter * @see com.google.javascript.rhino.ErrorReporter */ public final ErrorReporter setErrorReporter(ErrorReporter reporter) { if (sealed) onSealedMutation(); if (reporter == null) throw new IllegalArgumentException(); ErrorReporter old = getErrorReporter(); if (reporter == old) { return old; } Object listeners = propertyListeners; if (listeners != null) { firePropertyChangeImpl(listeners, errorReporterProperty, old, reporter); } this.errorReporter = reporter; return old; } /** * Get the current locale. Returns the default locale if none has * been set. * * @see java.util.Locale */ public final Locale getLocale() { if (locale == null) locale = Locale.getDefault(); return locale; } /** * Set the current locale. * * @see java.util.Locale */ public final Locale setLocale(Locale loc) { if (sealed) onSealedMutation(); Locale result = locale; locale = loc; return result; } /** * Register an object to receive notifications when a bound property * has changed * @see java.beans.PropertyChangeEvent * @see #removePropertyChangeListener(java.beans.PropertyChangeListener) * @param l the listener */ public final void addPropertyChangeListener(PropertyChangeListener l) { if (sealed) onSealedMutation(); propertyListeners = Kit.addListener(propertyListeners, l); } /** * Remove an object from the list of objects

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS> registered to receive * notification of changes to a bounded property * @see java.beans.PropertyChangeEvent * @see #addPropertyChangeListener(java.beans.PropertyChangeListener) * @param l the listener */ public final void removePropertyChangeListener(PropertyChangeListener l) { if (sealed) onSealedMutation(); propertyListeners = Kit.removeListener(propertyListeners, l); } /** * Notify any registered listeners that a bounded property has changed * @see #addPropertyChangeListener(java.beans.PropertyChangeListener) * @see #removePropertyChangeListener(java.beans.PropertyChangeListener) * @see java.beans.PropertyChangeListener * @see java.beans.PropertyChangeEvent * @param property the bound property * @param oldValue the old value * @param newValue the new value */ final void firePropertyChange(String property, Object oldValue, Object newValue) { Object listeners = propertyListeners; if (listeners != null) { firePropertyChangeImpl(listeners, property, oldValue, newValue); } } private void firePropertyChangeImpl(Object listeners, String property, Object oldValue, Object newValue) { for (int i = 0; ; ++i) { Object l = Kit.getListener(listeners, i); if (l == null) break; if (l instanceof PropertyChangeListener) { PropertyChangeListener pcl = (PropertyChangeListener)l; pcl.propertyChange(new PropertyChangeEvent( this, property, oldValue, newValue)); } } } /** * Report a warning using the error reporter for the current thread. * * @param message the warning message to report * @param sourceName a string describing the source, such as a filename * @param lineno the starting line number * @param lineSource the text of the line (may be null) * @param lineOffset the offset into lineSource where problem was detected * @see com.google.javascript.rhino.ErrorReporter */ public static void reportWarning(String message, String sourceName, int lineno, String lineSource, int lineOffset) { Context cx = Context.getContext(); cx.getErrorReporter().warning(message, sourceName, lineno, lineSource, lineOffset); } /** * Report

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS>p> * @param key the key used to index the value * @param value the value to save */ public final void putThreadLocal(Object key, Object value) { if (sealed) onSealedMutation(); if (hashtable == null) hashtable = new Hashtable<Object, Object>(); hashtable.put(key, value); } /** * Remove values from thread-local storage. * @param key the key for the entry to remove. * @since 1.5 release 2 */ public final void removeThreadLocal(Object key) { if (sealed) onSealedMutation(); if (hashtable == null) return; hashtable.remove(key); } /** * @deprecated * @see #FEATURE_DYNAMIC_SCOPE * @see #hasFeature(int) */ @Deprecated public final boolean hasCompileFunctionsWithDynamicScope() { return compileFunctionsWithDynamicScopeFlag; } /** * @deprecated * @see #FEATURE_DYNAMIC_SCOPE * @see #hasFeature(int) */ @Deprecated public final void setCompileFunctionsWithDynamicScope(boolean flag) { if (sealed) onSealedMutation(); compileFunctionsWithDynamicScopeFlag = flag; } /** * Return the debugger context data associated with current context. * @return the debugger data, or null if debugger is not attached */ public final Object getDebuggerContextData() { return debuggerData; } /** * Implementation of {@link Context#hasFeature(int featureIndex)}. * This can be used to customize {@link Context} without introducing * additional subclasses. */ protected boolean hasFeature(int featureIndex) { int version; switch (featureIndex) { case Context.FEATURE_NON_ECMA_GET_YEAR: /* * During the great date rewrite of 1.3, we tried to track the * evolving ECMA standard, which then had a definition of * getYear which always subtracted 1900. Which we * implemented, not realizing that it was incompatible with * the old behavior... now, rather than thrash the behavior * yet again, we've decided to leave it with the - 1

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS>"); } return cx; } final boolean isVersionECMA1() { return version == VERSION_DEFAULT || version >= VERSION_1_3; } static String getSourcePositionFromStack(int[] linep) { Context cx = getCurrentContext(); if (cx == null) return null; /** * A bit of a hack, but the only way to get filename and line * number from an enclosing frame. */ CharArrayWriter writer = new CharArrayWriter(); RuntimeException re = new RuntimeException(); re.printStackTrace(new PrintWriter(writer)); String s = writer.toString(); int open = -1; int close = -1; int colon = -1; for (int i=0; i < s.length(); i++) { char c = s.charAt(i); if (c == ':') colon = i; else if (c == '(') open = i; else if (c == ')') close = i; else if (c == '\n' && open != -1 && close != -1 && colon != -1 && open < colon && colon < close) { String fileStr = s.substring(open + 1, colon); if (!fileStr.endsWith(".java")) { String lineStr = s.substring(colon + 1, close); try { linep[0] = Integer.parseInt(lineStr); if (linep[0] < 0) { linep[0] = 0; } return fileStr; } catch (NumberFormatException e) { // fall through } } open = close = colon = -1; } } return null; } public final boolean isGeneratingDebugChanged() { return generatingDebugChanged; } /** * Add a name to the list of names forcing the creation of real * activation objects for functions. * * @param name the name of the object to add to the list */ public void addActivationName(String name) { if (sealed) onSealedMutation(); if (activationNames == null) activationNames = new Hashtable<Object, Object>(5); activationNames.put(name, name); } /** *

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS> "JSC_RESOLVED_TAG_EMPTY", "Could not resolve type in {0} tag of {1}"); static final DiagnosticType IMPLEMENTS_WITHOUT_CONSTRUCTOR = DiagnosticType.warning( "JSC_IMPLEMENTS_WITHOUT_CONSTRUCTOR", "@implements used without @constructor or @interface for {0}"); static final DiagnosticType VAR_ARGS_MUST_BE_LAST = DiagnosticType.warning( "JSC_VAR_ARGS_MUST_BE_LAST", "variable length argument must be last"); static final DiagnosticType OPTIONAL_ARG_AT_END = DiagnosticType.warning( "JSC_OPTIONAL_ARG_AT_END", "optional arguments must be at the end"); static final DiagnosticType INEXISTANT_PARAM = DiagnosticType.warning( "JSC_INEXISTANT_PARAM", "parameter {0} does not appear in {1}''s parameter list"); static final DiagnosticType TYPE_REDEFINITION = DiagnosticType.warning( "JSC_TYPE_REDEFINITION", "attempted re-definition of type {0}\n" + "found : {1}\n" + "expected: {2}"); static final DiagnosticType TEMPLATE_TYPE_DUPLICATED = DiagnosticType.error( "JSC_TEMPLATE_TYPE_DUPLICATED", "Only one parameter type must be the template type"); static final DiagnosticType TEMPLATE_TYPE_EXPECTED = DiagnosticType.error( "JSC_TEMPLATE_TYPE_EXPECTED", "The template type must be a parameter type"); static final DiagnosticType THIS_TYPE_NON_OBJECT = DiagnosticType.warning( "JSC_THIS_TYPE_NON_OBJECT", "@this type of a function must be an object\n" + "Actual type: {0}"); private class ExtendedTypeValidator implements Predicate<JSType> { @Override public boolean apply(JSType type) { ObjectType objectType = ObjectType.cast(type); if (objectType == null) { reportWarning(EXTENDS_NON_OBJECT, fnName, type.toString()); } else if ( objectType.isEmptyType() || (objectType.isUnknownType() && // If this has a supertype that hasn't been resolved yet, // then we can assume this type will be

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS> ok once the super // type resolves. (objectType.getImplicitPrototype() == null || objectType.getImplicitPrototype().isResolved()))) { reportWarning(RESOLVED_TAG_EMPTY, "@extends", fnName); } else { return true; } return false; } } private class ImplementedTypeValidator implements Predicate<JSType> { @Override public boolean apply(JSType type) { ObjectType objectType = ObjectType.cast(type); if (objectType == null) { reportError(BAD_IMPLEMENTED_TYPE, fnName); } else if ( objectType.isEmptyType() || (objectType.isUnknownType() && // If this has a supertype that hasn't been resolved yet, // then we can assume this type will be ok once the super // type resolves. (objectType.getImplicitPrototype() == null || objectType.getImplicitPrototype().isResolved()))) { reportWarning(RESOLVED_TAG_EMPTY, "@implements", fnName); } else { return true; } return false; } } private class ThisTypeValidator implements Predicate<JSType> { @Override public boolean apply(JSType type) { // TODO(user): Doing an instanceof check here is too // restrictive as (Date,Error) is, for instance, an object type // even though its implementation is a UnionType. Would need to // create interfaces JSType, ObjectType, FunctionType etc and have // separate implementation instead of the class hierarchy, so that // union types can also be object types, etc. if (!type.restrictByNotNullOrUndefined().isSubtype( typeRegistry.getNativeType(OBJECT_TYPE))) { reportWarning(THIS_TYPE_NON_OBJECT, type.toString()); return false; } return true; } } /** * @param fnName The function name. * @param compiler The compiler. * @param errorRoot The node to associate with any warning generated by * this builder. * @param sourceName A source name for associating any warnings that * we have to emit. * @param scope The syntactic scope. */ FunctionTypeBuilder(String fnName, AbstractCompiler compiler, Node errorRoot, String sourceName, Scope

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS> method might right its var_args as individual // arguments. if (currentParam.getNext() != null && newParam.isVarArgs()) { newParam.setVarArgs(false); newParam.setOptionalArg(true); } } else { warnedAboutArgList |= addParameter( paramBuilder, typeRegistry.getNativeType(UNKNOWN_TYPE), warnedAboutArgList, codingConvention.isOptionalParameter(currentParam) || oldParamsListHitOptArgs, codingConvention.isVarArgsParameter(currentParam)); } } parametersNode = paramBuilder.build(); } return this; } /** * Infer the return type from JSDocInfo. */ FunctionTypeBuilder inferReturnType(@Nullable JSDocInfo info) { if (info != null && info.hasReturnType()) { returnType = info.getReturnType().evaluate(scope, typeRegistry); returnTypeInferred = false; } if (templateTypeName != null && returnType != null && returnType.restrictByNotNullOrUndefined().isTemplateType()) { reportError(TEMPLATE_TYPE_EXPECTED, fnName); } return this; } /** * If we haven't found a return value yet, try to look at the "return" * statements in the function. */ FunctionTypeBuilder inferReturnStatementsAsLastResort( @Nullable Node functionBlock) { if (functionBlock == null || compiler.getInput(sourceName).isExtern()) { return this; } Preconditions.checkArgument(functionBlock.getType() == Token.BLOCK); if (returnType == null) { boolean hasNonEmptyReturns = false; List<Node> worklist = Lists.newArrayList(functionBlock); while (!worklist.isEmpty()) { Node current = worklist.remove(worklist.size() - 1); int cType = current.getType(); if (cType == Token.RETURN && current.getFirstChild() != null) { hasNonEmptyReturns = true; break; } else if (NodeUtil.isStatementBlock(current) || NodeUtil.isControlStructure(current)) { for (Node child = current.getFirstChild(); child != null; child = child.getNext()) { worklist.add(child);

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS> } } } if (!hasNonEmptyReturns) { returnType = typeRegistry.getNativeType(VOID_TYPE); returnTypeInferred = true; } } return this; } /** * Infer the role of the function (whether it's a constructor or interface) * and what it inherits from in JSDocInfo. */ FunctionTypeBuilder inferInheritance(@Nullable JSDocInfo info) { if (info != null) { isConstructor = info.isConstructor(); isInterface = info.isInterface(); // base type if (info.hasBaseType()) { if (isConstructor || isInterface) { JSType maybeBaseType = info.getBaseType().evaluate(scope, typeRegistry); if (maybeBaseType != null && maybeBaseType.setValidator(new ExtendedTypeValidator())) { baseType = (ObjectType) maybeBaseType; } } else { reportWarning(EXTENDS_WITHOUT_TYPEDEF, fnName); } } // implemented interfaces if (isConstructor || isInterface) { implementedInterfaces = Lists.newArrayList(); for (JSTypeExpression t : info.getImplementedInterfaces()) { JSType maybeInterType = t.evaluate(scope, typeRegistry); if (maybeInterType != null && maybeInterType.setValidator(new ImplementedTypeValidator())) { implementedInterfaces.add((ObjectType) maybeInterType); } } if (baseType != null) { JSType maybeFunctionType = baseType.getConstructor(); if (maybeFunctionType instanceof FunctionType) { FunctionType functionType = baseType.getConstructor(); Iterables.addAll( implementedInterfaces, functionType.getImplementedInterfaces()); } } } else if (info.getImplementedInterfaceCount() > 0) { reportWarning(IMPLEMENTS_WITHOUT_CONSTRUCTOR, fnName); } } return this; } /** * Infers the type of {@code this}. * @param type The type of this. */ FunctionTypeBuilder inferThisType(JSDocInfo info, JSType type) { // Look at the @this annotation first. inferThisType(info, (Node) null); if (thisType == null) { ObjectType objType = ObjectType.cast(type); if (

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS>objType != null && (info == null || !info.hasType())) { thisType = objType; } } return this; } /** * Infers the type of {@code this}. * @param info The JSDocInfo for this function. * @param owner The node for the object whose prototype "owns" this function. * For example, {@code A} in the expression {@code A.prototype.foo}. May * be null to indicate that this is not a prototype property. */ FunctionTypeBuilder inferThisType(JSDocInfo info, @Nullable Node owner) { ObjectType maybeThisType = null; if (info != null && info.hasThisType()) { maybeThisType = ObjectType.cast( info.getThisType().evaluate(scope, typeRegistry)); } if (maybeThisType != null) { thisType = maybeThisType; thisType.setValidator(new ThisTypeValidator()); } else if (owner != null && (info == null || !info.hasType())) { // If the function is of the form: // x.prototype.y = function() {} // then we can assume "x" is the @this type. On the other hand, // if it's of the form: // /** @type {Function} */ x.prototype.y; // then we should not give it a @this type. String ownerTypeName = owner.getQualifiedName(); Var ownerVar = scope.getVar(ownerTypeName); JSType ownerType = ownerVar == null ? null : ownerVar.getType(); FunctionType ownerFnType = ownerType instanceof FunctionType ? (FunctionType) ownerType : null; ObjectType instType = ownerFnType == null || ownerFnType.isOrdinaryFunction() ? null : ownerFnType.getInstanceType(); if (instType != null) { thisType = instType; } } return this; } /** * Infer the parameter types from the doc info alone. */ FunctionTypeBuilder inferParameterTypes(JSDocInfo info) { // Create a fake args parent. Node lp = new Node(Token.LP); for (String name : info.getParameterNames()) { lp.addChildToBack(Node.

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS>newString(Token.NAME, name)); } return inferParameterTypes(lp, info); } /** * Infer the parameter types from the list of argument names and * the doc info. */ FunctionTypeBuilder inferParameterTypes(@Nullable Node argsParent, @Nullable JSDocInfo info) { if (argsParent == null) { if (info == null) { return this; } else { return inferParameterTypes(info); } } // arguments Node oldParameterType = null; if (parametersNode != null) { oldParameterType = parametersNode.getFirstChild(); } FunctionParamBuilder builder = new FunctionParamBuilder(typeRegistry); boolean warnedAboutArgList = false; Set<String> allJsDocParams = (info == null) ? Sets.<String>newHashSet() : Sets.newHashSet(info.getParameterNames()); boolean foundTemplateType = false; for (Node arg : argsParent.children()) { String argumentName = arg.getString(); allJsDocParams.remove(argumentName); // type from JSDocInfo JSType parameterType = null; boolean isOptionalParam = isOptionalParameter(arg, info); boolean isVarArgs = isVarArgsParameter(arg, info); if (info != null && info.hasParameterType(argumentName)) { parameterType = info.getParameterType(argumentName).evaluate(scope, typeRegistry); } else if (oldParameterType != null && oldParameterType.getJSType() != null) { parameterType = oldParameterType.getJSType(); isOptionalParam = oldParameterType.isOptionalArg(); isVarArgs = oldParameterType.isVarArgs(); } else { parameterType = typeRegistry.getNativeType(UNKNOWN_TYPE); } if (templateTypeName != null && parameterType.restrictByNotNullOrUndefined().isTemplateType()) { if (foundTemplateType) { reportError(TEMPLATE_TYPE_DUPLICATED, fnName); } foundTemplateType = true; } warnedAboutArgList |= addParameter( builder, parameterType, warnedAboutArgList, isOptionalParam, isVarArgs); if (oldParameterType != null) { oldParameterType = oldParameterType.getNext(); } } if

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS> (templateTypeName != null && !foundTemplateType) { reportError(TEMPLATE_TYPE_EXPECTED, fnName); } for (String inexistentName : allJsDocParams) { reportWarning(INEXISTANT_PARAM, inexistentName, fnName); } parametersNode = builder.build(); return this; } /** * @return Whether the given param is an optional param. */ private boolean isOptionalParameter( Node param, @Nullable JSDocInfo info) { if (codingConvention.isOptionalParameter(param)) { return true; } String paramName = param.getString(); return info != null && info.hasParameterType(paramName) && info.getParameterType(paramName).isOptionalArg(); } /** * Determine whether this is a var args parameter. * @return Whether the given param is a var args param. */ private boolean isVarArgsParameter( Node param, @Nullable JSDocInfo info) { if (codingConvention.isVarArgsParameter(param)) { return true; } String paramName = param.getString(); return info != null && info.hasParameterType(paramName) && info.getParameterType(paramName).isVarArgs(); } /** * Infer the template type from the doc info. */ FunctionTypeBuilder inferTemplateTypeName(@Nullable JSDocInfo info) { if (info != null) { templateTypeName = info.getTemplateTypeName(); typeRegistry.setTemplateTypeName(templateTypeName); } return this; } /** * Add a parameter to the param list. * @param builder A builder. * @param paramType The parameter type. * @param warnedAboutArgList Whether we've already warned about arg ordering * issues (like if optional args appeared before required ones). * @param isOptional Is this an optional parameter? * @param isVarArgs Is this a var args parameter? * @return Whether a warning was emitted. */ private boolean addParameter(FunctionParamBuilder builder, JSType paramType, boolean warnedAboutArgList, boolean isOptional, boolean isVarArgs) { boolean emittedWarning = false; if (isOptional) { // Remembering that an optional parameter has been encountered //

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS> return fnType; } private void maybeSetBaseType(FunctionType fnType) { if (baseType != null) { fnType.setPrototypeBasedOn(baseType); } } /** * Returns a constructor function either by returning it from the * registry if it exists or creating and registering a new type. If * there is already a type, then warn if the existing type is * different than the one we are creating, though still return the * existing function if possible. The primary purpose of this is * that registering a constructor will fail for all built-in types * that are initialized in {@link JSTypeRegistry}. We a) want to * make sure that the type information specified in the externs file * matches what is in the registry and b) annotate the externs with * the {@link JSType} from the registry so that there are not two * separate JSType objects for one type. */ private FunctionType getOrCreateConstructor() { FunctionType fnType = typeRegistry.createConstructorType( fnName, sourceNode, parametersNode, returnType); JSType existingType = typeRegistry.getType(fnName); if (existingType != null) { boolean isInstanceObject = existingType instanceof InstanceObjectType; if (isInstanceObject || fnName.equals("Function")) { FunctionType existingFn = isInstanceObject ? ((InstanceObjectType) existingType).getConstructor() : typeRegistry.getNativeFunctionType(FUNCTION_FUNCTION_TYPE); if (existingFn.getSource() == null) { existingFn.setSource(sourceNode); } if (!existingFn.hasEqualCallType(fnType)) { reportWarning(TYPE_REDEFINITION, fnName, fnType.toString(), existingFn.toString()); } return existingFn; } else { // We fall through and return the created type, even though it will fail // to register. We have no choice as we have to return a function. We // issue an error elsewhere though, so the user should fix it. } } maybeSetBaseType(fnType); if (getScopeDeclaredIn().isGlobal() && !fnName.isEmpty()) { typeRegistry.declareType(fnName, fnType.getInstanceType()); } return fnType; } private void

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS> reportWarning(DiagnosticType warning, String ... args) { compiler.report(JSError.make(sourceName, errorRoot, warning, args)); } private void reportError(DiagnosticType error, String ... args) { compiler.report(JSError.make(sourceName, errorRoot, error, args)); } /** * Determines whether the given jsdoc info declares a function type. */ static boolean isFunctionTypeDeclaration(JSDocInfo info) { return info.getParameterCount() > 0 || info.hasReturnType() || info.hasThisType() || info.isConstructor() || info.isInterface(); } /** * The scope that we should declare this function in, if it needs * to be declared in a scope. Notice that TypedScopeCreator takes * care of most scope-declaring. */ private Scope getScopeDeclaredIn() { int dotIndex = fnName.indexOf("."); if (dotIndex != -1) { String rootVarName = fnName.substring(0, dotIndex); Var rootVar = scope.getVar(rootVarName); if (rootVar != null) { return rootVar.getScope(); } } return scope; } }

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS> reportBadModuleReference(name, ref); } } } } } private void reportBadModuleReference(Name name, Ref ref) { compiler.report( JSError.make(ref.sourceName, ref.node, STRICT_MODULE_DEP_QNAME, ref.module.getName(), name.declaration.module.getName(), name.fullName())); } private void reportRefToUndefinedName(Name name, Ref ref) { // grab the highest undefined ancestor to output in the warning message. while (name.parent != null && name.parent.globalSets + name.parent.localSets == 0) { name = name.parent; } // If this is an annotated EXPR-GET, don't do anything. Node parent = ref.node.getParent(); if (parent.getType() == Token.EXPR_RESULT) { JSDocInfo info = ref.node.getJSDocInfo(); if (info != null && info.hasTypedefType()) { return; } } compiler.report( JSError.make(ref.sourceName, ref.node, level, UNDEFINED_NAME_WARNING, name.fullName())); } /** * Checks whether the given name is a property, and whether that property * must be initialized with its full qualified name. */ private static boolean propertyMustBeInitializedByFullName(Name name) { // If an object literal in the global namespace is never aliased, // then all of its properties must be defined using its full qualified // name. This implies that its properties must all be in the global // namespace as well. // // The same is not true for FUNCTION and OTHER types, because their // implicit prototypes have properties that are not captured by the global // namespace. return name.parent != null && name.parent.aliasingGets == 0 && name.parent.type == Name.Type.OBJECTLIT; } }

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS>/* * Copyright 2004 The Closure Compiler Authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.google.javascript.jscomp; import com.google.javascript.jscomp.CompilerOptions.LanguageMode; import com.google.javascript.rhino.Node; import com.google.javascript.rhino.Token; import junit.framework.TestCase; public class CodePrinterTest extends TestCase { static Node parse(String js) { return parse(js, false); } static Node parse(String js, boolean checkTypes) { Compiler compiler = new Compiler(); CompilerOptions options = new CompilerOptions(); // Allow getters and setters. options.setLanguageIn(LanguageMode.ECMASCRIPT5); compiler.initOptions(options); Node n = compiler.parseTestCode(js); if (checkTypes) { DefaultPassConfig passConfig = new DefaultPassConfig(null); CompilerPass typeResolver = passConfig.resolveTypes.create(compiler); Node externs = new Node(Token.SCRIPT); Node externAndJsRoot = new Node(Token.BLOCK, externs, n); externAndJsRoot.setIsSyntheticBlock(true); typeResolver.process(externs, n); CompilerPass inferTypes = passConfig.inferTypes.create(compiler); inferTypes.process(externs, n); } checkUnexpectedErrorsOrWarnings(compiler, 0); return n; } private static void checkUnexpectedErrorsOrWarnings( Compiler compiler, int expected) { int actual = compiler.getErrors().length + compiler.getWarnings().length; if (actual != expected) { String msg = ""; for (JSError err : compiler.get

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS>Errors()) { msg += "Error:" + err.toString() + "\n"; } for (JSError err : compiler.getWarnings()) { msg += "Warning:" + err.toString() + "\n"; } assertEquals("Unexpected warnings or errors.\n " + msg, expected, actual); } } String parsePrint(String js, boolean prettyprint, int lineThreshold) { return new CodePrinter.Builder(parse(js)).setPrettyPrint(prettyprint) .setLineLengthThreshold(lineThreshold).build(); } String parsePrint(String js, boolean prettyprint, boolean lineBreak, int lineThreshold) { return new CodePrinter.Builder(parse(js)).setPrettyPrint(prettyprint) .setLineLengthThreshold(lineThreshold).setLineBreak(lineBreak).build(); } String parsePrint(String js, boolean prettyprint, boolean lineBreak, int lineThreshold, boolean outputTypes) { return new CodePrinter.Builder(parse(js, true)).setPrettyPrint(prettyprint) .setOutputTypes(outputTypes) .setLineLengthThreshold(lineThreshold).setLineBreak(lineBreak) .build(); } String parsePrint(String js, boolean prettyprint, boolean lineBreak, int lineThreshold, boolean outputTypes, boolean tagAsStrict) { return new CodePrinter.Builder(parse(js, true)).setPrettyPrint(prettyprint) .setOutputTypes(outputTypes) .setLineLengthThreshold(lineThreshold).setLineBreak(lineBreak) .setTagAsStrict(tagAsStrict) .build(); } String printNode(Node n) { return new CodePrinter.Builder(n).setLineLengthThreshold( CodePrinter.DEFAULT_LINE_LENGTH_THRESHOLD).build(); } void assertPrintNode(String expectedJs, Node ast) { assertEquals(expectedJs, printNode(ast)); } public void testPrint() { assertPrint("10 + a + b", "10+a+b"); assertPrint("10 + (30*50)", "10+30*50"); assertPrint("with(x) { x + 3; }", "with(x)x+3"); assertPrint("\"aa'a\"", "\"aa'a

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS> Kit.codeBug(); } return null; } private static class NumberNode extends Node { private static final long serialVersionUID = 1L; NumberNode(double number) { super(Token.NUMBER); this.number = number; } public NumberNode(double number, int lineno, int charno) { super(Token.NUMBER, lineno, charno); this.number = number; } @Override public double getDouble() { return this.number; } @Override public void setDouble(double d) { this.number = d; } @Override boolean isEquivalentTo(Node node, boolean compareJsType, boolean recurse) { return (super.isEquivalentTo(node, compareJsType, recurse) && getDouble() == ((NumberNode) node).getDouble()); } private double number; } private static class StringNode extends Node { private static final long serialVersionUID = 1L; StringNode(int type, String str) { super(type); if (null == str) { throw new IllegalArgumentException("StringNode: str is null"); } this.str = str; } StringNode(int type, String str, int lineno, int charno) { super(type, lineno, charno); if (null == str) { throw new IllegalArgumentException("StringNode: str is null"); } this.str = str; } /** * returns the string content. * @return non null. */ @Override public String getString() { return this.str; } /** * sets the string content. * @param str the new value. Non null. */ @Override public void setString(String str) { if (null == str) { throw new IllegalArgumentException("StringNode: str is null"); } this.str = str; } @Override boolean isEquivalentTo(Node node, boolean compareJsType, boolean recurse) { return (super.isEquivalentTo(node, compareJsType, recurse) && this.str.equals(((StringNode) node).str)); } /** * If the property is not defined, this was not a quoted key. The * QUOTED_PROP int property

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS> public Node(int nodeType, Node left, Node right, int lineno, int charno) { this(nodeType, left, right); sourcePosition = mergeLineCharNo(lineno, charno); } public Node(int nodeType, Node left, Node mid, Node right, int lineno, int charno) { this(nodeType, left, mid, right); sourcePosition = mergeLineCharNo(lineno, charno); } public Node(int nodeType, Node left, Node mid, Node mid2, Node right, int lineno, int charno) { this(nodeType, left, mid, mid2, right); sourcePosition = mergeLineCharNo(lineno, charno); } public Node(int nodeType, Node[] children, int lineno, int charno) { this(nodeType, children); sourcePosition = mergeLineCharNo(lineno, charno); } public Node(int nodeType, Node[] children) { this.type = nodeType; parent = null; if (children.length != 0) { this.first = children[0]; this.last = children[children.length - 1]; for (int i = 1; i < children.length; i++) { if (null != children[i - 1].next) { // fail early on loops. implies same node in array twice throw new IllegalArgumentException("duplicate child"); } children[i - 1].next = children[i]; Preconditions.checkArgument(children[i - 1].parent == null); children[i - 1].parent = this; } Preconditions.checkArgument(children[children.length - 1].parent == null); children[children.length - 1].parent = this; if (null != this.last.next) { // fail early on loops. implies same node in array twice throw new IllegalArgumentException("duplicate child"); } } } public static Node newNumber(double number) { return new NumberNode(number); } public static Node newNumber(double number, int lineno, int charno) { return new NumberNode(number, lineno, charno); } public static Node newString(String str) { return new StringNode(Token.

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS>PropsFrom(Node other) { Preconditions.checkState(this.propListHead == null, "Node has existing properties."); this.propListHead = other.propListHead; return this; } public void removeProp(int propType) { PropListItem result = removeProp(propListHead, propType); if (result != propListHead) { propListHead = result; } } /** * @param item The item to inspect * @param propType The property to look for * @return The replacement list if the property was removed, or * 'item' otherwise. */ private PropListItem removeProp(PropListItem item, int propType) { if (item == null) { return null; } else if (item.type == propType) { return item.next; } else { PropListItem result = removeProp(item.next, propType); if (result != item.next) { return new PropListItem( item.type, item.intValue, item.objectValue, result); } else { return item; } } } public Object getProp(int propType) { PropListItem item = lookupProperty(propType); if (item == null) { return null; } return item.objectValue; } public boolean getBooleanProp(int propType) { return getIntProp(propType) != 0; } /** * Returns the integer value for the property, or 0 if the property * is not defined. */ public int getIntProp(int propType) { PropListItem item = lookupProperty(propType); if (item == null) { return 0; } return item.intValue; } public int getExistingIntProp(int propType) { PropListItem item = lookupProperty(propType); if (item == null) { Kit.codeBug(); } return item.intValue; } public void putProp(int propType, Object value) { removeProp(propType); if (value != null) { propListHead = new PropListItem(propType, value, propListHead); } } public void putBooleanProp(int propType, boolean value)

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS> { putIntProp(propType, value ? 1 : 0); } public void putIntProp(int propType, int value) { removeProp(propType); if (value != 0) { propListHead = new PropListItem(propType, value, propListHead); } } // Gets all the property types, in sorted order. private int[] getSortedPropTypes() { int count = 0; for (PropListItem x = propListHead; x != null; x = x.next) { count++; } int[] keys = new int[count]; for (PropListItem x = propListHead; x != null; x = x.next) { count--; keys[count] = x.type; } Arrays.sort(keys); return keys; } public int getLineno() { return extractLineno(sourcePosition); } public int getCharno() { return extractCharno(sourcePosition); } /** Can only be called when <tt>getType() == TokenStream.NUMBER</tt> */ public double getDouble() throws UnsupportedOperationException { if (this.getType() == Token.NUMBER) { throw new IllegalStateException( "Number node not created with Node.newNumber"); } else { throw new UnsupportedOperationException(this + " is not a number node"); } } /** Can only be called when <tt>getType() == TokenStream.NUMBER</tt> */ public void setDouble(double s) throws UnsupportedOperationException { if (this.getType() == Token.NUMBER) { throw new IllegalStateException( "Number node not created with Node.newNumber"); } else { throw new UnsupportedOperationException(this + " is not a string node"); } } /** Can only be called when node has String context. */ public String getString() throws UnsupportedOperationException { if (this.getType() == Token.STRING) { throw new IllegalStateException( "String node not created with Node.newString"); } else { throw new UnsupportedOperationException(this + " is not a string node"); } } /** Can only be called when node has String context. */ public void setString(String s) throws UnsupportedOperationException { if (this.getType() == Token.STRING) { throw new IllegalStateException(

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS> "String node not created with Node.newString"); } else { throw new UnsupportedOperationException(this + " is not a string node"); } } @Override public String toString() { return toString(true, true, true); } public String toString( boolean printSource, boolean printAnnotations, boolean printType) { if (Token.printTrees) { StringBuilder sb = new StringBuilder(); toString(sb, printSource, printAnnotations, printType); return sb.toString(); } return String.valueOf(type); } private void toString( StringBuilder sb, boolean printSource, boolean printAnnotations, boolean printType) { if (Token.printTrees) { sb.append(Token.name(type)); if (this instanceof StringNode) { sb.append(' '); sb.append(getString()); } else if (type == Token.FUNCTION) { sb.append(' '); // In the case of JsDoc trees, the first child is often not a string // which causes exceptions to be thrown when calling toString or // toStringTree. if (first == null || first.getType() != Token.NAME) { sb.append("<invalid>"); } else { sb.append(first.getString()); } } else if (this instanceof ScriptOrFnNode) { ScriptOrFnNode sof = (ScriptOrFnNode) this; if (this instanceof FunctionNode) { FunctionNode fn = (FunctionNode) this; sb.append(' '); sb.append(fn.getFunctionName()); } if (printSource) { sb.append(" [source name: "); sb.append(sof.getSourceName()); sb.append("] [encoded source length: "); sb.append(sof.getEncodedSourceEnd() - sof.getEncodedSourceStart()); sb.append("] [base line: "); sb.append(sof.getBaseLineno()); sb.append("] [end line: "); sb.append(sof.getEndLineno()); sb.append(']'); } } else if (type == Token.NUMBER) { sb.append(' '); sb.append(getDouble()); } if (printSource) { int lineno = getLineno(); if (lineno != -

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS>1) { sb.append(' '); sb.append(lineno); } } if (printAnnotations) { int[] keys = getSortedPropTypes(); for (int i = 0; i < keys.length; i++) { int type = keys[i]; PropListItem x = lookupProperty(type); sb.append(" ["); sb.append(propToString(type)); sb.append(": "); String value; switch (type) { case TARGETBLOCK_PROP: // can't add this as it recurses value = "target block property"; break; case LOCAL_BLOCK_PROP: // can't add this as it is dull value = "last local block"; break; case ISNUMBER_PROP: switch (x.intValue) { case BOTH: value = "both"; break; case RIGHT: value = "right"; break; case LEFT: value = "left"; break; default: throw Kit.codeBug(); } break; case SPECIALCALL_PROP: switch (x.intValue) { case SPECIALCALL_EVAL: value = "eval"; break; case SPECIALCALL_WITH: value = "with"; break; default: // NON_SPECIALCALL should not be stored throw Kit.codeBug(); } break; default: Object obj = x.objectValue; if (obj != null) { value = obj.toString(); } else { value = String.valueOf(x.intValue); } break; } sb.append(value); sb.append(']'); } } if (printType) { if (jsType != null) { String jsTypeString = jsType.toString(); if (jsTypeString != null) { sb.append(" : "); sb.append(jsTypeString); } } } } } public String toStringTree() { return toStringTreeImpl(); } private String toStringTreeImpl() { try { StringBuilder s = new StringBuilder(); appendStringTree(s); return s.toString(); } catch (IOException e) { throw new RuntimeException("Should not happen\n" + e); } } public void appendStringTree(Append

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS>able appendable) throws IOException { toStringTreeHelper(this, 0, appendable); } private static void toStringTreeHelper(Node n, int level, Appendable sb) throws IOException { if (Token.printTrees) { for (int i = 0; i != level; ++i) { sb.append(" "); } sb.append(n.toString()); sb.append('\n'); for (Node cursor = n.getFirstChild(); cursor != null; cursor = cursor.getNext()) { toStringTreeHelper(cursor, level + 1, sb); } } } int type; // type of the node; Token.NAME for example Node next; // next sibling private Node first; // first element of a linked list of children private Node last; // last element of a linked list of children /** * Linked list of properties. Since vast majority of nodes would have * no more then 2 properties, linked list saves memory and provides * fast lookup. If this does not holds, propListHead can be replaced * by UintMap. */ private PropListItem propListHead; /** * COLUMN_BITS represents how many of the lower-order bits of * sourcePosition are reserved for storing the column number. * Bits above these store the line number. * This gives us decent position information for everything except * files already passed through a minimizer, where lines might * be longer than 4096 characters. */ public static final int COLUMN_BITS = 12; /** * MAX_COLUMN_NUMBER represents the maximum column number that can * be represented. JSCompiler's modifications to Rhino cause all * tokens located beyond the maximum column to MAX_COLUMN_NUMBER. */ public static final int MAX_COLUMN_NUMBER = (1 << COLUMN_BITS) - 1; /** * COLUMN_MASK stores a value where bits storing the column number * are set, and bits storing the line are not set. It's handy for * separating column number from line number. */ public static final int COLUMN_MASK = MAX_COLUMN_NUMBER; /** * Source position of this node. The position is encoded with the * column number in the low 12 bits of

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS> return "script"; case Token.EMPTY: return "empty"; case Token.GET_REF: return "get_ref"; case Token.REF_SPECIAL: return "ref_special"; } return "<unknown="+token+">"; } /** Returns true if this node is equivalent semantically to another */ public boolean isEquivalentTo(Node node) { return isEquivalentTo(node, false, true); } /** * Returns true if this node is equivalent semantically to another and * the types are equivalent. */ public boolean isEquivalentToTyped(Node node) { return isEquivalentTo(node, true, true); } /** * @param compareJsType Whether to compare the JSTypes of the nodes. * @param recurse Whether to compare the children of the current node, if * not only the the count of the children are compared. * @return Whether this node is equivalent semantically to the provided node. */ boolean isEquivalentTo(Node node, boolean compareJsType, boolean recurse) { if (type != node.getType() || getChildCount() != node.getChildCount() || getNodeClass(this) != getNodeClass(node)) { return false; } if (compareJsType && !JSType.isEquivalent(jsType, node.getJSType())) { return false; } if (type == Token.ARRAYLIT) { try { int[] indices1 = (int[]) getProp(Node.SKIP_INDEXES_PROP); int[] indices2 = (int[]) node.getProp(Node.SKIP_INDEXES_PROP); if (indices1 == null) { if (indices2 != null) { return false; } } else if (indices2 == null) { return false; } else if (indices1.length != indices2.length) { return false; } else { for (int i = 0; i < indices1.length; i++) { if (indices1[i] != indices2[i]) { return false; } } } } catch (Exception e) { return false; } } else if (type == Token.INC || type == Token.DEC) { int post1 = this.

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS> child != null; child = child.getNext()) { child.copyInformationFromForTree(other); } return this; } //========================================================================== // Custom annotations public JSType getJSType() { return jsType; } public void setJSType(JSType jsType) { this.jsType = jsType; } public FileLevelJsDocBuilder getJsDocBuilderForNode() { return new FileLevelJsDocBuilder(); } /** * An inner class that provides back-door access to the license * property of the JSDocInfo property for this node. This is only * meant to be used for top level script nodes where the * {@link com.google.javascript.jscomp.parsing.JsDocInfoParser} needs to * be able to append directly to the top level node, not just the * current node. */ public class FileLevelJsDocBuilder { public void append(String fileLevelComment) { JSDocInfo jsDocInfo = getJSDocInfo(); if (jsDocInfo == null) { // TODO(user): Is there a way to determine whether to // parse the JsDoc documentation from here? jsDocInfo = new JSDocInfo(false); } String license = jsDocInfo.getLicense(); if (license == null) { license = ""; } jsDocInfo.setLicense(license + fileLevelComment); setJSDocInfo(jsDocInfo); } } /** * Get the {@link JSDocInfo} attached to this node. * @return the information or {@code null} if no JSDoc is attached to this * node */ public JSDocInfo getJSDocInfo() { return (JSDocInfo) getProp(JSDOC_INFO_PROP); } /** * Sets the {@link JSDocInfo} attached to this node. */ public void setJSDocInfo(JSDocInfo info) { putProp(JSDOC_INFO_PROP, info); } /** * Sets whether this node is a variable length argument node. This * method is meaningful only on {@link Token#NAME} nodes * used to define a {@link Token#FUNCTION}'s argument list. */

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS> public void setVarArgs(boolean varArgs) { putBooleanProp(VAR_ARGS_NAME, varArgs); } /** * Returns whether this node is a variable length argument node. This * method's return value is meaningful only on {@link Token#NAME} nodes * used to define a {@link Token#FUNCTION}'s argument list. */ public boolean isVarArgs() { return getBooleanProp(VAR_ARGS_NAME); } /** * Sets whether this node is an optional argument node. This * method is meaningful only on {@link Token#NAME} nodes * used to define a {@link Token#FUNCTION}'s argument list. */ public void setOptionalArg(boolean optionalArg) { putBooleanProp(OPT_ARG_NAME, optionalArg); } /** * Returns whether this node is an optional argument node. This * method's return value is meaningful only on {@link Token#NAME} nodes * used to define a {@link Token#FUNCTION}'s argument list. */ public boolean isOptionalArg() { return getBooleanProp(OPT_ARG_NAME); } /** * Sets whether this is a synthetic block that should not be considered * a real source block. */ public void setIsSyntheticBlock(boolean val) { putBooleanProp(SYNTHETIC_BLOCK_PROP, val); } /** * Returns whether this is a synthetic block that should not be considered * a real source block. */ public boolean isSyntheticBlock() { return getBooleanProp(SYNTHETIC_BLOCK_PROP); } /** * Sets the ES5 directives on this node. */ public void setDirectives(Set<String> val) { putProp(DIRECTIVES, val); } /** * Returns the set of ES5 directives for this node. */ @SuppressWarnings("unchecked") public Set<String> getDirectives() { return (Set<String>) getProp(DIRECTIVES); } /** * Adds a warning to be suppressed. This is indistinguishable * from having a {@code @suppress} tag in the code. */ public void addSuppression(String warning) { if (getJSDocInfo() == null) { setJSDocInfo

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS>(new JSDocInfo(false)); } getJSDocInfo().addSuppression(warning); } /** * Sets whether this is a synthetic block that should not be considered * a real source block. */ public void setWasEmptyNode(boolean val) { putBooleanProp(EMPTY_BLOCK, val); } /** * Returns whether this is a synthetic block that should not be considered * a real source block. */ public boolean wasEmptyNode() { return getBooleanProp(EMPTY_BLOCK); } // There are four values of interest: // global state changes // this state changes // arguments state changes // whether the call throws an exception // locality of the result // We want a value of 0 to mean "global state changes and // unknown locality of result". final public static int FLAG_GLOBAL_STATE_UNMODIFIED = 1; final public static int FLAG_THIS_UNMODIFIED = 2; final public static int FLAG_ARGUMENTS_UNMODIFIED = 4; final public static int FLAG_NO_THROWS = 8; final public static int FLAG_LOCAL_RESULTS = 16; final public static int SIDE_EFFECTS_FLAGS_MASK = 31; final public static int SIDE_EFFECTS_ALL = 0; final public static int NO_SIDE_EFFECTS = FLAG_GLOBAL_STATE_UNMODIFIED | FLAG_THIS_UNMODIFIED | FLAG_ARGUMENTS_UNMODIFIED | FLAG_NO_THROWS; /** * Marks this function or constructor call's side effect flags. * This property is only meaningful for {@link Token#CALL} and * {@link Token#NEW} nodes. */ public void setSideEffectFlags(int flags) { Preconditions.checkArgument( getType() == Token.CALL || getType() == Token.NEW, "setIsNoSideEffectsCall only supports CALL and NEW nodes, got " + Token.name(getType())); putIntProp(SIDE_EFFECT_FLAGS, flags); } public void setSideEffectFlags(SideEffectFlags flags) { setSideEffectFlags(flags.valueOf()); } /** * Returns the side effects flags for this node.

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS> */ public int getSideEffectFlags() { return getIntProp(SIDE_EFFECT_FLAGS); } /** * A helper class for getting and setting the side-effect flags. * @author johnlenz@google.com (John Lenz) */ public static class SideEffectFlags { private int value = Node.SIDE_EFFECTS_ALL; public SideEffectFlags() { } public SideEffectFlags(int value) { this.value = value; } public int valueOf() { return value; } /** All side-effect occur and the returned results are non-local. */ public void setAllFlags() { value = Node.SIDE_EFFECTS_ALL; } /** No side-effects occur and the returned results are local. */ public void clearAllFlags() { value = Node.NO_SIDE_EFFECTS | Node.FLAG_LOCAL_RESULTS; } public boolean areAllFlagsSet() { return value == Node.SIDE_EFFECTS_ALL; } /** * Preserve the return result flag, but clear the others: * no global state change, no throws, no this change, no arguments change */ public void clearSideEffectFlags() { value |= Node.NO_SIDE_EFFECTS; } public void setMutatesGlobalState() { // Modify global means everything must be assumed to be modified. removeFlag(Node.FLAG_GLOBAL_STATE_UNMODIFIED); removeFlag(Node.FLAG_ARGUMENTS_UNMODIFIED); removeFlag(Node.FLAG_THIS_UNMODIFIED); } public void setThrows() { removeFlag(Node.FLAG_NO_THROWS); } public void setMutatesThis() { removeFlag(Node.FLAG_THIS_UNMODIFIED); } public void setMutatesArguments() { removeFlag(Node.FLAG_ARGUMENTS_UNMODIFIED); } public void setReturnsTainted() { removeFlag(Node.FLAG_LOCAL_RESULTS); } private void removeFlag(int flag) { value &= ~flag; } } /** * @return Whether the only side-effect is "modifies this" */ public boolean isOnlyModifiesThisCall() {

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS> return areBitFlagsSet( getSideEffectFlags() & Node.NO_SIDE_EFFECTS, Node.FLAG_GLOBAL_STATE_UNMODIFIED | Node.FLAG_ARGUMENTS_UNMODIFIED | Node.FLAG_NO_THROWS); } /** * Returns true if this node is a function or constructor call that * has no side effects. */ public boolean isNoSideEffectsCall() { return areBitFlagsSet(getSideEffectFlags(), NO_SIDE_EFFECTS); } /** * Returns true if this node is a function or constructor call that * returns a primitive or a local object (an object that has no other * references). */ public boolean isLocalResultCall() { return areBitFlagsSet(getSideEffectFlags(), FLAG_LOCAL_RESULTS); } /** * returns true if all the flags are set in value. */ private boolean areBitFlagsSet(int value, int flags) { return (value & flags) == flags; } /** * This should only be called for STRING nodes children of OBJECTLIT. */ public boolean isQuotedString() { return false; } /** * This should only be called for STRING nodes children of OBJECTLIT. */ public void setQuotedString() { Kit.codeBug(); } static class NodeMismatch { final Node nodeA; final Node nodeB; NodeMismatch(Node nodeA, Node nodeB) { this.nodeA = nodeA; this.nodeB = nodeB; } @Override public boolean equals(Object object) { if (object instanceof NodeMismatch) { NodeMismatch that = (NodeMismatch) object; return that.nodeA.equals(this.nodeA) && that.nodeB.equals(this.nodeB); } return false; } @Override public int hashCode() { return Objects.hashCode(nodeA, nodeB); } } }

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS> public boolean isNullable() { return false; } @Override public TernaryValue testForEquality(JSType that) { TernaryValue result = super.testForEquality(that); if (result != null) { return result; } if (that.isUnknownType() || that.isSubtype( getNativeType(JSTypeNative.OBJECT_NUMBER_STRING_BOOLEAN))) { return UNKNOWN; } return FALSE; } @Override public boolean isNumberValueType() { return true; } @Override public boolean matchesNumberContext() { return true; } @Override public boolean matchesStringContext() { return true; } @Override public boolean matchesObjectContext() { // TODO(user): Revisit this for ES4, which is stricter. return true; } @Override public String toString() { return getDisplayName(); } @Override public String getDisplayName() { return "number"; } @Override public BooleanLiteralSet getPossibleToBooleanOutcomes() { return BooleanLiteralSet.BOTH; } @Override public <T> T visit(Visitor<T> visitor) { return visitor.caseNumberType(); } @Override public JSType autoboxesTo() { return getNativeType(JSTypeNative.NUMBER_OBJECT_TYPE); } }

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS> constructor, false); } InstanceObjectType(JSTypeRegistry registry, FunctionType constructor, boolean isNativeType) { super(registry, null, null, isNativeType); Preconditions.checkNotNull(constructor); this.constructor = constructor; } @Override public String getReferenceName() { return getConstructor().getReferenceName(); } @Override public boolean hasReferenceName() { return getConstructor().hasReferenceName(); } @Override public ObjectType getImplicitPrototype() { return getConstructor().getPrototype(); } @Override public FunctionType getConstructor() { return constructor; } @Override boolean defineProperty(String name, JSType type, boolean inferred, boolean inExterns, Node propertyNode) { ObjectType proto = getImplicitPrototype(); if (proto != null && proto.hasOwnDeclaredProperty(name)) { return false; } return super.defineProperty(name, type, inferred, inExterns, propertyNode); } @Override public String toString() { if (constructor.hasReferenceName()) { return constructor.getReferenceName(); } else { return super.toString(); } } @Override boolean isTheObjectType() { return getConstructor().isNative() && "Object".equals(getReferenceName()); } @Override public boolean isInstanceType() { return true; } @Override public boolean isArrayType() { return getConstructor().isNative() && "Array".equals(getReferenceName()); } @Override public boolean isStringObjectType() { return getConstructor().isNative() && "String".equals(getReferenceName()); } @Override public boolean isBooleanObjectType() { return getConstructor().isNative() && "Boolean".equals(getReferenceName()); } @Override public boolean isNumberObjectType() { return getConstructor().isNative() && "Number".equals(getReferenceName()); } @Override public boolean isDateType() { return getConstructor().isNative() && "Date".equals(getReferenceName()); } @Override public boolean isRegexpType() { return getConstructor().isNative() && "RegExp".equals(getReferenceName()); } @Override public boolean isNominalType() { return hasReferenceName(); } @Override public boolean

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS>ErrorReporter(null, new String[] { "first warning" }); * ... * assertTrue(e.hasEncounteredAllWarnings()); * </pre> * */ public final class TestErrorReporter extends Assert implements ErrorReporter { private String[] errors; private String[] warnings; private int errorsIndex = 0; private int warningsIndex = 0; public TestErrorReporter(String[] errors, String[] warnings) { this.errors = errors; this.warnings = warnings; } public static TestErrorReporter forNoExpectedReports() { return new TestErrorReporter(null, null); } public void setErrors(String[] errors) { this.errors = errors; errorsIndex = 0; } public void setWarnings(String[] warnings) { this.warnings = warnings; warningsIndex = 0; } public void error(String message, String sourceName, int line, String lineSource, int lineOffset) { if (errors != null && errorsIndex < errors.length) { assertEquals(errors[errorsIndex++], message); } else { fail("extra error: " + message); } } public void warning(String message, String sourceName, int line, String lineSource, int lineOffset) { if (warnings != null && warningsIndex < warnings.length) { assertEquals(warnings[warningsIndex++], message); } else { fail("extra warning: " + message); } } public EvaluatorException runtimeError(String message, String sourceName, int line, String lineSource, int lineOffset) { throw new UnsupportedOperationException(); } /** * Returns whether all warnings were reported to this reporter. */ public boolean hasEncounteredAllWarnings() { return (warnings == null) ? warningsIndex == 0 : warnings.length == warningsIndex; } /** * Returns whether all errors were reported to this reporter. */ public boolean hasEncounteredAllErrors() { return (errors == null) ? errorsIndex == 0 : errors.length == errorsIndex; } }

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS>; NullType(JSTypeRegistry registry) { super(registry); } @Override public boolean isNullType() { return true; } @Override public boolean isNullable() { return true; } @Override public boolean matchesNumberContext() { return true; } @Override public boolean matchesObjectContext() { return false; } @Override public boolean matchesStringContext() { return true; } @Override public JSType restrictByNotNullOrUndefined() { return registry.getNativeType(JSTypeNative.NO_TYPE); } @Override public TernaryValue testForEquality(JSType that) { TernaryValue result = super.testForEquality(that); if (result != null) { return result; } if (that.isNullType() || that.isVoidType()) { return TRUE; } if (that.isUnknownType() || that.isNullable()) { return UNKNOWN; } return FALSE; } @Override public String toString() { return getDisplayName(); } @Override public String getDisplayName() { return "null"; } @Override public BooleanLiteralSet getPossibleToBooleanOutcomes() { return BooleanLiteralSet.FALSE; } @Override public <T> T visit(Visitor<T> visitor) { return visitor.caseNullType(); } }

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS> template type. private TemplateType templateType; private final boolean tolerateUndefinedValues; /** * The type registry has three modes, which control how type ASTs are * converted to types in {@link #createFromTypeNodes}. */ public static enum ResolveMode { /** * Expressions are converted into Unknown blobs that can be * resolved into complex types. */ LAZY_EXPRESSIONS, /** * Expressions are evaluated. If any names in the expression point to * unknown types, then we create a proxy {@code NamedType} structure * until the type can be resolved. * * This is the legacy way of resolving ways, and may not exist in the * future. */ LAZY_NAMES, /** * Expressions and type names are evaluated aggressively. A warning * will be emitted if a type name fails to resolve to a real type. */ IMMEDIATE } private ResolveMode resolveMode = ResolveMode.LAZY_NAMES; /** * Constructs a new type registry populated with the built-in types. */ public JSTypeRegistry(ErrorReporter reporter) { this(reporter, false); } /** * Constructs a new type registry populated with the built-in types. */ public JSTypeRegistry( ErrorReporter reporter, boolean tolerateUndefinedValues) { this.reporter = reporter; nativeTypes = new JSType[JSTypeNative.values().length]; namesToTypes = new HashMap<String, JSType>(); resetForTypeCheck(); this.tolerateUndefinedValues = tolerateUndefinedValues; } /** * Set the current resolving mode of the type registry. * @see ResolveMode */ public void setResolveMode(ResolveMode mode) { this.resolveMode = mode; } ResolveMode getResolveMode() { return resolveMode; } public ErrorReporter getErrorReporter() { return reporter; } public boolean shouldTolerateUndefinedValues() { return tolerateUndefinedValues; } /** * Reset to run the TypeCheck pass. */ public void resetForTypeCheck() { typesIndexedByProperty.clear(); eachRefTypeIndexedByProperty.clear(); initializeBuiltInTypes(); namesToTypes.clear(); namespaces.clear(); initializeRegistry();

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS> register(getNativeType(JSTypeNative.TYPE_ERROR_TYPE)); register(getNativeType(JSTypeNative.RANGE_ERROR_TYPE)); register(getNativeType(JSTypeNative.REFERENCE_ERROR_TYPE)); register(getNativeType(JSTypeNative.SYNTAX_ERROR_TYPE)); register(getNativeType(JSTypeNative.REGEXP_TYPE)); register(getNativeType(JSTypeNative.STRING_OBJECT_TYPE)); register(getNativeType(JSTypeNative.STRING_TYPE)); register(getNativeType(JSTypeNative.VOID_TYPE)); register(getNativeType(JSTypeNative.VOID_TYPE), "Undefined"); register(getNativeType(JSTypeNative.VOID_TYPE), "void"); register(getNativeType(JSTypeNative.FUNCTION_INSTANCE_TYPE), "Function"); } private void register(JSType type) { register(type, type.toString()); } private void register(JSType type, String name) { namesToTypes.put(name, type); // Add all the namespaces in which this name lives. while (name.indexOf('.') > 0) { name = name.substring(0, name.lastIndexOf('.')); namespaces.add(name); } } private void registerNativeType(JSTypeNative typeId, JSType type) { nativeTypes[typeId.ordinal()] = type; } /** * Tells the type system that {@code owner} may have a property named * {@code propertyName}. This allows the registry to keep track of what * types a property is defined upon. * * This is NOT the same as saying that {@code owner} must have a property * named type. ObjectType#hasProperty attempts to minimize false positives * ("if we're not sure, then don't type check this property"). The type * registry, on the other hand, should attempt to minimize false negatives * ("if this property is assigned anywhere in the program, it must * show up in the type registry"). */ public void registerPropertyOnType(String propertyName, JSType type) { UnionTypeBuilder typeSet = typesIndexedByProperty.get(propertyName); if (typeSet == null) { typeSet = new UnionTypeBuilder(this); typesIndexed

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS> FunctionParamBuilder builder = new FunctionParamBuilder(this); builder.addOptionalParams(parameterTypes); return builder.build(); } /** * Creates a tree hierarchy representing a typed argument list. * * @param lastVarArgs whether the last type should considered as a variable * length argument. * @param parameterTypes the parameter types. The last element of this array * is considered a variable length argument is {@code lastVarArgs} is * {@code true}. * @return a tree hierarchy representing a typed argument list */ private Node createParameters(boolean lastVarArgs, JSType... parameterTypes) { FunctionParamBuilder builder = new FunctionParamBuilder(this); int max = parameterTypes.length - 1; for (int i = 0; i <= max; i++) { if (lastVarArgs && i == max) { builder.addVarArgs(parameterTypes[i]); } else { builder.addRequiredParams(parameterTypes[i]); } } return builder.build(); } /** * Creates a function type. * @param returnType the function's return type * @param lastVarArgs whether the last parameter type should be considered as * an extensible var_args parameter * @param parameterTypes the parameters' types */ public FunctionType createFunctionType(JSType returnType, boolean lastVarArgs, JSType... parameterTypes) { if (lastVarArgs) { return createFunctionTypeWithVarArgs(returnType, parameterTypes); } else { return createFunctionType(returnType, parameterTypes); } } /** * Creates a new function type based on an existing function type but * with a new return type. * @param existingFunctionType the existing function type. * @param returnType the new return type. */ public FunctionType createFunctionTypeWithNewReturnType( FunctionType existingFunctionType, JSType returnType) { return new FunctionBuilder(this) .copyFromOtherFunction(existingFunctionType) .withReturnType(returnType) .build(); } /** * Creates a new function type based on an existing function type but * with a new {@code this} type. * @param existingFunctionType the existing function type. * @param thisType the new this type. */ public FunctionType createFunctionTypeWith

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS> = current.getFirstChild(); for (Node arg = current.getFirstChild(); arg != null; arg = arg.getNext()) { if (arg.getType() == Token.ELLIPSIS) { if (arg.getChildCount() == 0) { paramBuilder.addVarArgs(getNativeType(UNKNOWN_TYPE)); } else { paramBuilder.addVarArgs( createFromTypeNodesInternal( arg.getFirstChild(), sourceName, scope)); } } else { JSType type = createFromTypeNodesInternal( arg, sourceName, scope); if (arg.getType() == Token.EQUALS) { boolean addSuccess = paramBuilder.addOptionalParams(type); if (!addSuccess) { reporter.warning( ScriptRuntime.getMessage0("msg.jsdoc.function.varargs"), sourceName, arg.getLineno(), "", arg.getCharno()); } } else { paramBuilder.addRequiredParams(type); } } } current = current.getNext(); } JSType returnType = createFromTypeNodesInternal(current, sourceName, scope); return new FunctionBuilder(this) .withParams(paramBuilder) .withReturnType(returnType) .withTypeOfThis(thisType) .setIsConstructor(isConstructor) .build(); } throw new IllegalStateException( "Unexpected node in type expression: " + n.toString()); } /** * Creates a RecordType from the nodes representing said record type. * @param n The node with type info. * @param sourceName The source file name. * @param scope A scope for doing type name lookups. */ private JSType createRecordTypeFromNodes(Node n, String sourceName, StaticScope<JSType> scope) { RecordTypeBuilder builder = new RecordTypeBuilder(this); // For each of the fields in the record type. for (Node fieldTypeNode = n.getFirstChild(); fieldTypeNode != null; fieldTypeNode = fieldTypeNode.getNext()) { // Get the property's name. Node fieldNameNode = fieldTypeNode; boolean hasType = false; if (fieldTypeNode.getType() == Token.COLON) { fieldNameNode = fieldTypeNode.getFirstChild(); hasType = true;

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS> } String fieldName = fieldNameNode.getString(); // TODO(user): Move this into the lexer/parser. // Remove the string literal characters around a field name, // if any. if (fieldName.startsWith("'") || fieldName.startsWith("\"")) { fieldName = fieldName.substring(1, fieldName.length() - 1); } // Get the property's type. JSType fieldType = null; if (hasType) { // We have a declared type. fieldType = createFromTypeNodesInternal( fieldTypeNode.getLastChild(), sourceName, scope); } else { // Otherwise, the type is UNKNOWN. fieldType = getNativeType(JSTypeNative.UNKNOWN_TYPE); } // Add the property to the record. if (builder.addProperty(fieldName, fieldType, fieldNameNode) == null) { // Duplicate field name, warning and skip reporter.warning( "Duplicate record field " + fieldName, sourceName, n.getLineno(), "", fieldNameNode.getCharno()); } } return builder.build(); } /** * Sets the template type name. */ public void setTemplateTypeName(String name) { templateTypeName = name; templateType = new TemplateType(this, name); } /** * Clears the template type name. */ public void clearTemplateTypeName() { templateTypeName = null; templateType = null; } }

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS> {@code JSDocInfo} * object being created. * */ final public class JSDocInfoBuilder { // the current JSDoc which is being populated private JSDocInfo currentInfo; // whether the current JSDocInfo has valuable information private boolean populated = false; // whether to include the documentation itself when parsing the JsDoc private boolean parseDocumentation = false; // the current marker, if any. private JSDocInfo.Marker currentMarker = null; public JSDocInfoBuilder(boolean parseDocumentation) { this.currentInfo = new JSDocInfo(parseDocumentation); this.parseDocumentation = parseDocumentation; } /** * Sets the original JSDoc comment string. This is a no-op if the builder * isn't configured to record documentation. */ public void recordOriginalCommentString(String sourceComment) { if (parseDocumentation) { currentInfo.setOriginalCommentString(sourceComment); } } public boolean shouldParseDocumentation() { return parseDocumentation; } /** * Returns whether this builder is populated with information that can be * used to {@link #build} a {@link JSDocInfo} object. */ public boolean isPopulated() { return populated; } /** * Returns whether this builder is populated with information that can be * used to {@link #build} a {@link JSDocInfo} object that has a * fileoverview tag. */ public boolean isPopulatedWithFileOverview() { return isPopulated() && (currentInfo.hasFileOverview() || currentInfo.isExterns() || currentInfo.isNoCompile()); } /** * Returns whether this builder recorded a description. */ public boolean isDescriptionRecorded() { return currentInfo.getDescription() != null; } /** * Builds a {@link JSDocInfo} object based on the populated information and * returns it. Once this method is called, the builder can be reused to build * another {@link JSDocInfo} object. * * @param sourceName The source file containing the JSDoc. * @return a {@link JSDocInfo} object populated with the values given to this * builder. If no value was populated, this method simply

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS> returns * {@code null} */ public JSDocInfo build(String sourceName) { if (populated) { JSDocInfo built = currentInfo; built.setSourceName(sourceName); populateDefaults(built); populated = false; currentInfo = new JSDocInfo(this.parseDocumentation); return built; } else { return null; } } /** Generate defaults when certain parameters are not specified. */ private static void populateDefaults(JSDocInfo info) { if (info.getVisibility() == null) { info.setVisibility(Visibility.INHERITED); } } /** * Adds a marker to the current JSDocInfo and populates the marker with the * annotation information. */ public void markAnnotation(String annotation, int lineno, int charno) { JSDocInfo.Marker marker = currentInfo.addMarker(); if (marker != null) { marker.annotation = new JSDocInfo.StringPosition(); marker.annotation.setItem(annotation); marker.annotation.setPositionInformation(lineno, charno, lineno, charno + annotation.length()); } currentMarker = marker; } /** * Adds a textual block to the current marker. */ public void markText(String text, int startLineno, int startCharno, int endLineno, int endCharno) { if (currentMarker != null) { currentMarker.description = new JSDocInfo.StringPosition(); currentMarker.description.setItem(text); currentMarker.description.setPositionInformation(startLineno, startCharno, endLineno, endCharno); } } /** * Adds a type declaration to the current marker. */ public void markTypeNode(Node typeNode, int lineno, int startCharno, int endCharno, boolean hasLC) { if (currentMarker != null) { currentMarker.type = new JSDocInfo.TypePosition(); currentMarker.type.setItem(typeNode); currentMarker.type.hasBrackets = hasLC; currentMarker.type.setPositionInformation(lineno, startCharno, lineno, endCharno); } } /** * Adds a name declaration to

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS> the current marker. */ public void markName(String name, int lineno, int charno) { if (currentMarker != null) { currentMarker.name = new JSDocInfo.StringPosition(); currentMarker.name.setItem(name); currentMarker.name.setPositionInformation(lineno, charno, lineno, charno + name.length()); } } /** * Records a block-level description. * * @return {@code true} if the description was recorded. */ public boolean recordBlockDescription(String description) { populated = true; return currentInfo.documentBlock(description); } /** * Records a visibility. * * @return {@code true} if the visibility was recorded and {@code false} * if it was already defined */ public boolean recordVisibility(Visibility visibility) { if (currentInfo.getVisibility() == null) { populated = true; currentInfo.setVisibility(visibility); return true; } else { return false; } } /** * Records a typed parameter. * * @return {@code true} if the typed parameter was recorded and * {@code false} if a parameter with the same name was already defined */ public boolean recordParameter(String parameterName, JSTypeExpression type) { if (!hasAnySingletonTypeTags() && currentInfo.declareParam(type, parameterName)) { populated = true; return true; } else { return false; } } /** * Records a parameter's description. * * @return {@code true} if the parameter's description was recorded and * {@code false} if a parameter with the same name was already defined */ public boolean recordParameterDescription( String parameterName, String description) { if (currentInfo.documentParam(parameterName, description)) { populated = true; return true; } else { return false; } } /** * Records a template type name. * * @return {@code true} if the template type name was recorded and * {@code false} if a template type name was already defined. */ public boolean recordTemplateTypeName(String name) { if (currentInfo.declareTemplateTypeName(name)) { populated =

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS>; import java.util.Comparator; /** * Represents JavaScript value types.<p> * * Types are split into two separate families: value types and object types. * * A special {@link UnknownType} exists to represent a wildcard type on which * no information can be gathered. In particular, it can assign to everyone, * is a subtype of everyone (and everyone is a subtype of it).<p> * * If you remove the {@link UnknownType}, the set of types in the type system * forms a lattice with the {@link #isSubtype} relation defining the partial * order of types. All types are united at the top of the lattice by the * {@link AllType} and at the bottom by the {@link NoType}.<p> * */ public abstract class JSType implements Serializable { private static final long serialVersionUID = 1L; private boolean resolved = false; private JSType resolveResult = null; public static final String UNKNOWN_NAME = "Unknown class name"; public static final String NOT_A_CLASS = "Not declared as a constructor"; public static final String NOT_A_TYPE = "Not declared as a type name"; public static final String EMPTY_TYPE_COMPONENT = "Named type with empty name component"; /** * Total ordering on types based on their textual representation. * This is used to have a deterministic output of the toString * method of the union type since this output is used in tests. */ static final Comparator<JSType> ALPHA = new Comparator<JSType>() { public int compare(JSType t1, JSType t2) { return t1.toString().compareTo(t2.toString()); } }; // A flag set on enum definition tree nodes public static final int ENUMDECL = 1; public static final int NOT_ENUMDECL = 0; final JSTypeRegistry registry; JSType(JSTypeRegistry registry) { this.registry = registry; } /** * Utility method for less verbose code. */ JSType getNativeType(JSTypeNative typeId) { return registry.getNativeType(typeId); } /** * Gets the docInfo for this type. By default, documentation cannot be * attached to arbitrary

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS> types. This must be overridden for * programmer-defined types. */ public JSDocInfo getJSDocInfo() { return null; } /** * Returns a user meaningful label for the JSType instance. For example, * Functions and Enums will return their declaration name (if they have one). * Some types will not have a meaningful display name. Calls to * hasDisplayName() will return true IFF getDisplayName() will return null * or a zero length string. * * @return the display name of the type, or null if one is not available */ public String getDisplayName() { return null; } /** * @return true if the JSType has a user meaningful label. */ public boolean hasDisplayName() { String displayName = getDisplayName(); return displayName != null && !displayName.isEmpty(); } public boolean isNoType() { return false; } public boolean isNoResolvedType() { return false; } public boolean isNoObjectType() { return false; } public final boolean isEmptyType() { return isNoType() || isNoObjectType() || isNoResolvedType() || (registry.getNativeFunctionType( JSTypeNative.LEAST_FUNCTION_TYPE) == this); } public boolean isNumberObjectType() { return false; } public boolean isNumberValueType() { return false; } /** Whether this is the prototype of a function. */ public boolean isFunctionPrototypeType() { return false; } public boolean isStringObjectType() { return false; } boolean isTheObjectType() { return false; } public boolean isStringValueType() { return false; } /** * Tests whether the type is a string (value or Object). * @return {@code this &lt;: (String, string)} */ public final boolean isString() { return this.isSubtype( getNativeType(JSTypeNative.STRING_VALUE_OR_OBJECT_TYPE)); } /** * Tests whether the type is a number (value or Object). * @return {@code this &lt;: (Number, number)} */ public final boolean isNumber() { return this.isSubtype( getNativeType(JSTypeNative.NUMBER_VALUE

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS>toString()); } } public void visit(NodeTraversal traversal, Node node, Node parent) { Collection<Definition> defs = passUnderTest.getDefinitionsReferencedAt(node); if (defs != null) { StringBuilder sb = new StringBuilder(); sb.append("USE "); sb.append(Token.name(node.getType())); sb.append(" "); sb.append(node.getQualifiedName()); sb.append(" -> "); Multiset<String> defstrs = TreeMultiset.create(); for (Definition def : defs) { String defstr; Node rValue = def.getRValue(); if (rValue != null) { defstr = Token.name(rValue.getType()); } else { defstr = "<null>"; } if (def.isExtern()) { defstr = "EXTERN " + defstr; } defstrs.add(defstr); } sb.append(defstrs.toString()); found.add(sb.toString()); } } } }

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS>util.Map; import java.util.Set; /** * <p>JSDoc information describing JavaScript code. JSDoc is represented as a * unified object with fields for each JSDoc annotation, even though some * combinations are incorrect. For instance, if a JSDoc describes an enum, * it cannot have information about a return type. This implementation * takes advantage of such incompatibilities to reuse fields for multiple * purposes, reducing memory consumption.</p> * * <p>Constructing {@link JSDocInfo} objects is simplified by * {@link JSDocInfoBuilder} which provides early incompatibility detection.</p> * */ public class JSDocInfo implements Serializable { private static final long serialVersionUID = 1L; /** * Visibility categories. The {@link Visibility#ordinal()} can be used as a * numerical indicator of privacy, where 0 is the most private. This means * that the {@link Visibility#compareTo} method can be used to * determine if a visibility is more permissive than another. */ public enum Visibility { PRIVATE, PROTECTED, PUBLIC, // If visibility is not specified, we just assume that visibility // is inherited from the super class. INHERITED } private static final class LazilyInitializedInfo implements Serializable { private static final long serialVersionUID = 1L; // Function information JSTypeExpression baseType = null; List<JSTypeExpression> implementedInterfaces = null; Map<String, JSTypeExpression> parameters = null; List<JSTypeExpression> thrownTypes = null; String templateTypeName = null; // Other information String description = null; String meaning = null; String deprecated = null; String license = null; Set<String> suppressions = null; Set<String> modifies = null; String lendsName = null; } private static final class LazilyInitializedDocumentation { String sourceComment = null; List<Marker> markers = null; Map<String, String> parameters = null; Map<JSTypeExpression, String> throwsDescriptions = null; String blockDescription = null; String fileOverview = null; String returnDescription = null; String version = null; List<String> authors = null;

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS> MASK_NOSIDEEFFECTS = 0x00004000; // @nosideeffects private static final int MASK_EXTERNS = 0x00008000; // @externs private static final int MASK_JAVADISPATCH = 0x00010000; // @javadispath private static final int MASK_NOCOMPILE = 0x00020000; // @nocompile // 3 bit type field stored in the top 3 bits of the most significant // nibble. private static final int MASK_TYPEFIELD = 0xE0000000; // 1110... private static final int TYPEFIELD_TYPE = 0x20000000; // 0010... private static final int TYPEFIELD_RETURN = 0x40000000; // 0100... private static final int TYPEFIELD_ENUM = 0x60000000; // 0110... private static final int TYPEFIELD_TYPEDEF = 0x80000000; // 1000... /** * Creates a {@link JSDocInfo} object. This object should be created using * a {@link JSDocInfoBuilder}. */ JSDocInfo(boolean includeDocumentation) { this.includeDocumentation = includeDocumentation; } // Visible for testing. public JSDocInfo() {} void setConstant(boolean value) { setFlag(value, MASK_CONSTANT); } void setConstructor(boolean value) { setFlag(value, MASK_CONSTRUCTOR); } void setDefine(boolean value) { setFlag(value, MASK_DEFINE); } void setHidden(boolean value) { setFlag(value, MASK_HIDDEN); } void setNoCheck(boolean value) { setFlag(value, MASK_NOCHECK); } void setShouldPreserveTry(boolean value) { setFlag(value, MASK_PRESERVETRY); } void setOverride(boolean value) { setFlag(value, MASK_OVERRIDE);

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS> } void setNoAlias(boolean value) { setFlag(value, MASK_NOALIAS); } // Visible for testing. public void setDeprecated(boolean value) { setFlag(value, MASK_DEPRECATED); } void setInterface(boolean value) { setFlag(value, MASK_INTERFACE); } void setExport(boolean value) { setFlag(value, MASK_EXPORT); } void setNoShadow(boolean value) { setFlag(value, MASK_NOSHADOW); } void setImplicitCast(boolean value) { setFlag(value, MASK_IMPLICITCAST); } void setNoSideEffects(boolean value) { setFlag(value, MASK_NOSIDEEFFECTS); } void setExterns(boolean value) { setFlag(value, MASK_EXTERNS); } void setJavaDispatch(boolean value) { setFlag(value, MASK_JAVADISPATCH); } void setNoCompile(boolean value) { setFlag(value, MASK_NOCOMPILE); } private void setFlag(boolean value, int mask) { if (value) { bitset |= mask; } else { bitset &= ~mask; } } /** * Returns whether the {@code @const} annotation is present on this * {@link JSDocInfo}. */ public boolean isConstant() { return getFlag(MASK_CONSTANT) || isDefine(); } /** * Returns whether the {@code @constructor} annotation is present on this * {@link JSDocInfo}. */ public boolean isConstructor() { return getFlag(MASK_CONSTRUCTOR); } /** * Returns whether the {@code @define} annotation is present on this * {@link JSDocInfo}. If this annotation is present, then the * {@link #getType()} method will retrieve the define type. */ public boolean isDefine() { return getFlag(MASK_DEFINE); } /** * Returns whether the {@code @hidden} annotation is present on this * {@link JSDocInfo}. */ public boolean isHidden() { return getFlag(MASK_HIDDEN); } /** * Returns whether the {@code @nocheck}

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS> assignments. */ public String getLendsName() { return (info == null) ? null : info.lendsName; } void setLendsName(String name) { lazyInitInfo(); info.lendsName = name; } /** * Gets the description specified by the {@code @license} annotation. */ public String getLicense() { return (info == null) ? null : info.license; } /** License directives can appear in multiple comments, and always * apply to the entire file. Break protection and allow outsiders to * update the license string so that we can attach the license text even * when the JSDocInfo has been created and tagged with other information. * @param license String containing new license text. */ public void setLicense(String license) { lazyInitInfo(); info.license = license; } @Override public String toString() { return "JSDocInfo"; } /** * Returns whether this {@link JSDocInfo} contains a type for {@code @extends} * annotation. */ public boolean hasBaseType() { return getBaseType() != null; } /** * Adds an implemented interface. Returns whether the interface was added. If * the interface was already present in the list, it won't get added again. */ boolean addImplementedInterface(JSTypeExpression interfaceName) { lazyInitInfo(); if (info.implementedInterfaces == null) { info.implementedInterfaces = Lists.newArrayListWithCapacity(2); } if (info.implementedInterfaces.contains(interfaceName)) { return false; } info.implementedInterfaces.add(interfaceName); return true; } /** * Returns the types specified by the {@code @implements} annotation. * * @return An immutable list of JSTypeExpression objects that can * be resolved to types. */ public List<JSTypeExpression> getImplementedInterfaces() { if (info == null || info.implementedInterfaces == null) { return ImmutableList.of(); } return Collections.unmodifiableList(info.implementedInterfaces); } /** * Gets the number of interfaces specified by the {@code @implements} * annotation. */ public int getImplementedInterfaceCount() { if

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS> all types declared. Declares newly discovered types * and type properties in the type registry. */ public Scope createScope(Node root, Scope parent) { // Constructing the global scope is very different than constructing // inner scopes, because only global scopes can contain named classes that // show up in the type registry. Scope newScope = null; AbstractScopeBuilder scopeBuilder = null; if (parent == null) { // Find all the classes in the global scope. newScope = createInitialScope(root); GlobalScopeBuilder globalScopeBuilder = new GlobalScopeBuilder(newScope); scopeBuilder = globalScopeBuilder; NodeTraversal.traverse(compiler, root, scopeBuilder); } else { newScope = new Scope(parent, root); LocalScopeBuilder localScopeBuilder = new LocalScopeBuilder(newScope); scopeBuilder = localScopeBuilder; localScopeBuilder.build(); } scopeBuilder.resolveStubDeclarations(); scopeBuilder.resolveTypes(); // Gather the properties in each function that we found in the // global scope, if that function has a @this type that we can // build properties on. for (Node functionNode : scopeBuilder.nonExternFunctions) { JSType type = functionNode.getJSType(); if (type != null && type instanceof FunctionType) { FunctionType fnType = (FunctionType) type; ObjectType fnThisType = fnType.getTypeOfThis(); if (!fnThisType.isUnknownType()) { NodeTraversal.traverse(compiler, functionNode.getLastChild(), scopeBuilder.new CollectProperties(fnThisType)); } } } if (parent == null) { codingConvention.defineDelegateProxyPrototypeProperties( typeRegistry, newScope, delegateProxyPrototypes); } return newScope; } /** * Create the outermost scope. This scope contains native binding such as * {@code Object}, {@code Date}, etc. */ @VisibleForTesting Scope createInitialScope(Node root) { NodeTraversal.traverse( compiler, root, new DiscoverEnumsAndTypedefs(typeRegistry)); Scope s = new Scope(root, compiler); declareNativeFunctionType(s, ARRAY_FUNCTION_TYPE); declareNativeFunctionType(s, BOOLEAN_OBJECT_FUNCTION_TYPE); declareNative

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS>FunctionType(s, DATE_FUNCTION_TYPE); declareNativeFunctionType(s, ERROR_FUNCTION_TYPE); declareNativeFunctionType(s, EVAL_ERROR_FUNCTION_TYPE); declareNativeFunctionType(s, FUNCTION_FUNCTION_TYPE); declareNativeFunctionType(s, NUMBER_OBJECT_FUNCTION_TYPE); declareNativeFunctionType(s, OBJECT_FUNCTION_TYPE); declareNativeFunctionType(s, RANGE_ERROR_FUNCTION_TYPE); declareNativeFunctionType(s, REFERENCE_ERROR_FUNCTION_TYPE); declareNativeFunctionType(s, REGEXP_FUNCTION_TYPE); declareNativeFunctionType(s, STRING_OBJECT_FUNCTION_TYPE); declareNativeFunctionType(s, SYNTAX_ERROR_FUNCTION_TYPE); declareNativeFunctionType(s, TYPE_ERROR_FUNCTION_TYPE); declareNativeFunctionType(s, URI_ERROR_FUNCTION_TYPE); declareNativeValueType(s, "undefined", VOID_TYPE); // The typedef construct needs the any type, so that it can be assigned // to anything. This is kind of a hack, and an artifact of the typedef // syntax we've chosen. declareNativeValueType(s, LEGACY_TYPEDEF, NO_TYPE); // ActiveXObject is unqiuely special, because it can be used to construct // any type (the type that it creates is related to the arguments you // pass to it). declareNativeValueType(s, "ActiveXObject", NO_OBJECT_TYPE); return s; } private void declareNativeFunctionType(Scope scope, JSTypeNative tId) { FunctionType t = typeRegistry.getNativeFunctionType(tId); declareNativeType(scope, t.getInstanceType().getReferenceName(), t); declareNativeType( scope, t.getPrototype().getReferenceName(), t.getPrototype()); } private void declareNativeValueType(Scope scope, String name, JSTypeNative tId) { declareNativeType(scope, name, typeRegistry.getNativeType(tId)); } private void declareNativeType(Scope scope, String name, JSType t) { scope.declare(name, null, t, null, false); } private static class DiscoverEnumsAndTypedefs extends AbstractShallowStatementCallback { private final JSTypeRegistry registry; DiscoverEnumsAndTypedefs(JSType

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS>Registry registry) { this.registry = registry; } @Override public void visit(NodeTraversal t, Node node, Node parent) { Node nameNode = null; switch (node.getType()) { case Token.VAR: for (Node child = node.getFirstChild(); child != null; child = child.getNext()) { identifyNameNode( child, child.getFirstChild(), NodeUtil.getInfoForNameNode(child)); } break; case Token.EXPR_RESULT: Node firstChild = node.getFirstChild(); if (firstChild.getType() == Token.ASSIGN) { identifyNameNode( firstChild.getFirstChild(), firstChild.getLastChild(), firstChild.getJSDocInfo()); } else { identifyNameNode( firstChild, null, firstChild.getJSDocInfo()); } break; } } private void identifyNameNode( Node nameNode, Node valueNode, JSDocInfo info) { if (nameNode.isQualifiedName()) { if (info != null) { if (info.hasEnumParameterType()) { registry.identifyNonNullableName(nameNode.getQualifiedName()); } else if (info.hasTypedefType()) { registry.identifyNonNullableName(nameNode.getQualifiedName()); } } if (valueNode != null && LEGACY_TYPEDEF.equals(valueNode.getQualifiedName())) { registry.identifyNonNullableName(nameNode.getQualifiedName()); } } } } /** * Given a node, determines whether that node names a prototype * property, and if so, returns the qualified name node representing * the owner of that property. Otherwise, returns null. */ private static Node getPrototypePropertyOwner(Node n) { if (n.getType() == Token.GETPROP) { Node firstChild = n.getFirstChild(); if (firstChild.getType() == Token.GETPROP && firstChild.getLastChild().getString().equals("prototype")) { Node maybeOwner = firstChild.getFirstChild(); if (maybeOwner.isQualifiedName()) { return maybeOwner; } } } return null; } private JSType getNativeType(JSTypeNative nativeType) { return typeRegistry.getNativeType(

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS> case Token.NUMBER: // Defer keys to the Token.OBJECTLIT case if (!NodeUtil.isObjectLitKey(n, n.getParent())) { n.setJSType(getNativeType(NUMBER_TYPE)); } break; case Token.TRUE: case Token.FALSE: n.setJSType(getNativeType(BOOLEAN_TYPE)); break; case Token.REGEXP: n.setJSType(getNativeType(REGEXP_TYPE)); break; case Token.REF_SPECIAL: n.setJSType(getNativeType(UNKNOWN_TYPE)); break; case Token.OBJECTLIT: defineObjectLiteral(t, n); break; // NOTE(nicksantos): If we ever support Array tuples, // we will need to put ARRAYLIT here as well. } } private void defineObjectLiteral(NodeTraversal t, Node objectLit) { // Handle the @lends annotation. JSType type = null; JSDocInfo info = objectLit.getJSDocInfo(); if (info != null && info.getLendsName() != null) { String lendsName = info.getLendsName(); Var lendsVar = scope.getVar(lendsName); if (lendsVar == null) { compiler.report( JSError.make(sourceName, objectLit, UNKNOWN_LENDS, lendsName)); } else { type = lendsVar.getType(); if (type == null) { type = typeRegistry.getNativeType(UNKNOWN_TYPE); } if (!type.isSubtype(typeRegistry.getNativeType(OBJECT_TYPE))) { compiler.report( JSError.make(sourceName, objectLit, LENDS_ON_NON_OBJECT, lendsName, type.toString())); type = null; } else { objectLit.setJSType(type); } } } info = getBestJSDocInfo(objectLit); Node lValue = getBestLValue(objectLit); String lValueName = getBestLValueName(lValue); boolean createdEnumType = false; if (info != null && info.hasEnumParameterType()) { type = createEnumTypeFromNodes

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS>(objectLit, lValueName, info, lValue); createdEnumType = true; } if (type == null) { type = typeRegistry.createAnonymousObjectType(); } setDeferredType(objectLit, type); // If this is an enum, the properties were already taken care of above. if (!createdEnumType) { processObjectLitProperties( t, objectLit, ObjectType.cast(objectLit.getJSType())); } } /** * Process an object literal and all the types on it. * @param objLit The OBJECTLIT node. * @param objLitType The type of the OBJECTLIT node. This might be a named * type, because of the lends annotation. */ void processObjectLitProperties( NodeTraversal t, Node objLit, ObjectType objLitType) { for (Node keyNode = objLit.getFirstChild(); keyNode != null; keyNode = keyNode.getNext()) { Node value = keyNode.getFirstChild(); String memberName = NodeUtil.getObjectLitKeyName(keyNode); JSDocInfo info = keyNode.getJSDocInfo(); JSType valueType = getDeclaredType( t.getSourceName(), info, keyNode, value); JSType keyType = NodeUtil.getObjectLitKeyTypeFromValueType( keyNode, valueType); if (keyType != null) { // Try to declare this property in the current scope if it // has an authoritative name. String qualifiedName = getBestLValueName(keyNode); if (qualifiedName != null) { defineSlot(keyNode, objLit, qualifiedName, keyType, false); } else { setDeferredType(keyNode, keyType); } if (objLitType != null) { // Declare this property on its object literal. boolean isExtern = t.getInput() != null && t.getInput().isExtern(); objLitType.defineDeclaredProperty( memberName, keyType, isExtern, keyNode); } } } } /** * Returns the type specified in a JSDoc annotation near a GETPROP or NAME. * * Extracts type information from either the {@code @type} tag or from * the {@code @return} and

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS> {@code @param} tags. */ private JSType getDeclaredTypeInAnnotation(String sourceName, Node node, JSDocInfo info) { JSType jsType = null; Node objNode = node.getType() == Token.GETPROP ? node.getFirstChild() : NodeUtil.isObjectLitKey(node, node.getParent()) ? node.getParent() : null; if (info != null) { if (info.hasType()) { jsType = info.getType().evaluate(scope, typeRegistry); } else if (FunctionTypeBuilder.isFunctionTypeDeclaration(info)) { String fnName = node.getQualifiedName(); jsType = createFunctionTypeFromNodes( null, fnName, info, node); } } return jsType; } /** * Asserts that it's ok to define this node's name. * The node should have a source name and be of the specified type. */ void assertDefinitionNode(Node n, int type) { Preconditions.checkState(sourceName != null); Preconditions.checkState(n.getType() == type); } /** * Defines a catch parameter. */ void defineCatch(Node n, Node parent) { assertDefinitionNode(n, Token.CATCH); Node catchName = n.getFirstChild(); defineSlot(catchName, n, null); } /** * Defines a VAR initialization. */ void defineVar(Node n, Node parent) { assertDefinitionNode(n, Token.VAR); JSDocInfo info = n.getJSDocInfo(); if (n.hasMoreThanOneChild()) { if (info != null) { // multiple children compiler.report(JSError.make(sourceName, n, MULTIPLE_VAR_DEF)); } for (Node name : n.children()) { defineName(name, n, parent, name.getJSDocInfo()); } } else { Node name = n.getFirstChild(); defineName(name, n, parent, (info != null) ? info : name.getJSDocInfo()); } } /** * Defines a function literal. */ void defineFunctionLiteral(Node n, Node parent) { assertDefinitionNode(n, Token.

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS>FUNCTION); // Determine the name and JSDocInfo and lvalue for the function. // Any of these may be null. Node lValue = getBestLValue(n); JSDocInfo info = getBestJSDocInfo(n); String functionName = getBestLValueName(lValue); FunctionType functionType = createFunctionTypeFromNodes(n, functionName, info, lValue); // Assigning the function type to the function node setDeferredType(n, functionType); // Declare this symbol in the current scope iff it's a function // declaration. Otherwise, the declaration will happen in other // code paths. if (NodeUtil.isFunctionDeclaration(n)) { defineSlot(n.getFirstChild(), n, functionType); } } /** * Defines a variable based on the {@link Token#NAME} node passed. * @param name The {@link Token#NAME} node. * @param var The parent of the {@code name} node, which must be a * {@link Token#VAR} node. * @param parent {@code var}'s parent. * @param info the {@link JSDocInfo} information relating to this * {@code name} node. */ private void defineName(Node name, Node var, Node parent, JSDocInfo info) { Node value = name.getFirstChild(); // variable's type JSType type = getDeclaredType(sourceName, info, name, value); if (type == null) { // The variable's type will be inferred. CompilerInput input = compiler.getInput(sourceName); Preconditions.checkNotNull(input, sourceName); type = input.isExtern() ? getNativeType(UNKNOWN_TYPE) : null; } defineSlot(name, var, type); } /** * If a variable is assigned a function literal in the global scope, * make that a declared type (even if there's no doc info). * There's only one exception to this rule: * if the return type is inferred, and we're in a local * scope, we should assume the whole function is inferred. */ private boolean shouldUseFunctionLiteralType( FunctionType type, JSDocInfo info, Node lValue) { if

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS> (info != null) { return true; } if (lValue != null && NodeUtil.isObjectLitKey(lValue, lValue.getParent())) { return false; } return scope.isGlobal() || !type.isReturnTypeInferred(); } /** * Creates a new function type, based on the given nodes. * * This handles two cases that are semantically very different, but * are not mutually exclusive: * - A function literal that needs a type attached to it. * - An assignment expression with function-type info in the jsdoc. * * All parameters are optional, and we will do the best we can to create * a function type. * * This function will always create a function type, so only call it if * you're sure that's what you want. * * @param rValue The function node. * @param name the function's name * @param info the {@link JSDocInfo} attached to the function definition * @param lvalueNode The node where this function is being * assigned. For example, {@code A.prototype.foo = ...} would be used to * determine that this function is a method of A.prototype. May be * null to indicate that this is not being assigned to a qualified name. */ private FunctionType createFunctionTypeFromNodes( @Nullable Node rValue, @Nullable String name, @Nullable JSDocInfo info, @Nullable Node lvalueNode) { FunctionType functionType = null; // Global ctor aliases should be registered with the type registry. if (rValue != null && rValue.isQualifiedName() && scope.isGlobal()) { Var var = scope.getVar(rValue.getQualifiedName()); if (var != null && var.getType() instanceof FunctionType) { FunctionType aliasedType = (FunctionType) var.getType(); if ((aliasedType.isConstructor() || aliasedType.isInterface()) && !aliasedType.isNativeObjectType()) { functionType = aliasedType; if (name != null && scope.isGlobal()) { typeRegistry.declareType(name, functionType.getInstanceType()); } } } } if (functionType == null) { Node error

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS>Root = rValue == null ? lvalueNode : rValue; boolean isFnLiteral = rValue != null && rValue.getType() == Token.FUNCTION; Node fnRoot = isFnLiteral ? rValue : null; Node parametersNode = isFnLiteral ? rValue.getFirstChild().getNext() : null; Node fnBlock = isFnLiteral ? parametersNode.getNext() : null; if (info != null && info.hasType()) { JSType type = info.getType().evaluate(scope, typeRegistry); // Known to be not null since we have the FUNCTION token there. type = type.restrictByNotNullOrUndefined(); if (type.isFunctionType()) { functionType = (FunctionType) type; functionType.setJSDocInfo(info); } } if (functionType == null) { // Find the type of any overridden function. FunctionType overriddenPropType = null; if (lvalueNode != null && lvalueNode.getType() == Token.GETPROP && lvalueNode.isQualifiedName()) { Var var = scope.getVar( lvalueNode.getFirstChild().getQualifiedName()); if (var != null) { ObjectType ownerType = ObjectType.cast(var.getType()); if (ownerType != null) { String propName = lvalueNode.getLastChild().getString(); overriddenPropType = findOverriddenFunction(ownerType, propName); } } } FunctionTypeBuilder builder = new FunctionTypeBuilder(name, compiler, errorRoot, sourceName, scope) .setSourceNode(fnRoot) .inferFromOverriddenFunction(overriddenPropType, parametersNode) .inferTemplateTypeName(info) .inferReturnType(info) .inferInheritance(info); // Infer the context type. boolean searchedForThisType = false; if (lvalueNode != null && lvalueNode.getType() == Token.GETPROP) { Node objNode = lvalueNode.getFirstChild(); if (objNode.getType() == Token.GETPROP && objNode.getLastChild().getString().equals("prototype")) { builder.inferThisType(info, objNode.getFirstChild()); searchedForThisType = true; } else if (objNode.getType() == Token

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS>.THIS) { builder.inferThisType(info, objNode.getJSType()); searchedForThisType = true; } } if (!searchedForThisType) { builder.inferThisType(info, (Node) null); } functionType = builder .inferParameterTypes(parametersNode, info) .inferReturnStatementsAsLastResort(fnBlock) .buildAndRegister(); } } // all done return functionType; } /** * Find the function that's being overridden on this type, if any. */ private FunctionType findOverriddenFunction( ObjectType ownerType, String propName) { // First, check to see if the property is implemented // on a superclass. JSType propType = ownerType.getPropertyType(propName); if (propType instanceof FunctionType) { return (FunctionType) propType; } else { // If it's not, then check to see if it's implemented // on an implemented interface. for (ObjectType iface : ownerType.getCtorImplementedInterfaces()) { propType = iface.getPropertyType(propName); if (propType instanceof FunctionType) { return (FunctionType) propType; } } } return null; } /** * Creates a new enum type, based on the given nodes. * * This handles two cases that are semantically very different, but * are not mutually exclusive: * - An object literal that needs an enum type attached to it. * - An assignment expression with an enum tag in the jsdoc. * * This function will always create an enum type, so only call it if * you're sure that's what you want. * * @param rValue The node of the enum. * @param name The enum's name * @param info The {@link JSDocInfo} attached to the enum definition. * @param lvalueNode The node where this function is being * assigned. */ private EnumType createEnumTypeFromNodes(Node rValue, String name, JSDocInfo info, Node lValueNode) { Preconditions.checkNotNull(info); Preconditions.checkState(info.hasEnumParameterType()); EnumType enumType = null; if (rValue != null &&

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS> rValue.isQualifiedName()) { // Handle an aliased enum. Var var = scope.getVar(rValue.getQualifiedName()); if (var != null && var.getType() instanceof EnumType) { enumType = (EnumType) var.getType(); } } if (enumType == null) { JSType elementsType = info.getEnumParameterType().evaluate(scope, typeRegistry); enumType = typeRegistry.createEnumType(name, elementsType); if (rValue != null && rValue.getType() == Token.OBJECTLIT) { // collect enum elements Node key = rValue.getFirstChild(); while (key != null) { String keyName = NodeUtil.getStringValue(key); if (keyName == null) { // GET and SET don't have a String value; compiler.report( JSError.make(sourceName, key, ENUM_NOT_CONSTANT, keyName)); } else if (enumType.hasOwnProperty(keyName)) { compiler.report(JSError.make(sourceName, key, ENUM_DUP, keyName)); } else if (!codingConvention.isValidEnumKey(keyName)) { compiler.report( JSError.make(sourceName, key, ENUM_NOT_CONSTANT, keyName)); } else { enumType.defineElement(keyName, key); } key = key.getNext(); } } } if (name != null && scope.isGlobal()) { typeRegistry.declareType(name, enumType.getElementsType()); } return enumType; } /** * Defines a typed variable. The defining node will be annotated with the * variable's type or {@code null} if its type is inferred. * @param name the defining node. It must be a {@link Token#NAME}. * @param parent the {@code name}'s parent. * @param type the variable's type. It may be {@code null}, in which case * the variable's type will be inferred. */ private void defineSlot(Node name, Node parent, JSType type) { defineSlot(name, parent, type, type == null); } /** * Defines a typed variable. The defining node will be annotated with the * variable's type

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS> * @param lValue The l-value node. * @param rValue The node that {@code n} is being initialized to, * or {@code null} if this is a stub declaration. */ private JSType getDeclaredType(String sourceName, JSDocInfo info, Node lValue, @Nullable Node rValue) { if (info != null && info.hasType()) { return getDeclaredTypeInAnnotation(sourceName, lValue, info); } else if (rValue != null && rValue.getType() == Token.FUNCTION && shouldUseFunctionLiteralType( (FunctionType) rValue.getJSType(), info, lValue)) { return rValue.getJSType(); } else if (info != null) { if (info.hasEnumParameterType()) { if (rValue != null && rValue.getType() == Token.OBJECTLIT) { return rValue.getJSType(); } else { return createEnumTypeFromNodes( rValue, lValue.getQualifiedName(), info, lValue); } } else if (info.isConstructor() || info.isInterface()) { return createFunctionTypeFromNodes( rValue, lValue.getQualifiedName(), info, lValue); } else { // Check if this is constant, and if it has a known type. if (info.isConstant()) { JSType knownType = null; if (rValue != null) { if (rValue.getJSType() != null && !rValue.getJSType().isUnknownType()) { return rValue.getJSType(); } else if (rValue.getType() == Token.OR) { // Check for a very specific JS idiom: // var x = x || TYPE; // This is used by Closure's base namespace for esoteric // reasons. Node firstClause = rValue.getFirstChild(); Node secondClause = firstClause.getNext(); boolean namesMatch = firstClause.getType() == Token.NAME && lValue.getType() == Token.NAME && firstClause.getString().equals(lValue.getString()); if (namesMatch && secondClause.getJSType() != null && !secondClause.getJSType().isUnknownType()) { return secondClause.getJSType

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS>.report(JSError.make(t.getSourceName(), n, CONSTRUCTOR_EXPECTED)); } } } /** * Apply special properties that only apply to delegates. */ private void applyDelegateRelationship( DelegateRelationship delegateRelationship) { ObjectType delegatorObject = ObjectType.cast( typeRegistry.getType(delegateRelationship.delegator)); ObjectType delegateBaseObject = ObjectType.cast( typeRegistry.getType(delegateRelationship.delegateBase)); ObjectType delegateSuperObject = ObjectType.cast( typeRegistry.getType(codingConvention.getDelegateSuperclassName())); if (delegatorObject != null && delegateBaseObject != null && delegateSuperObject != null) { FunctionType delegatorCtor = delegatorObject.getConstructor(); FunctionType delegateBaseCtor = delegateBaseObject.getConstructor(); FunctionType delegateSuperCtor = delegateSuperObject.getConstructor(); if (delegatorCtor != null && delegateBaseCtor != null && delegateSuperCtor != null) { FunctionParamBuilder functionParamBuilder = new FunctionParamBuilder(typeRegistry); functionParamBuilder.addRequiredParams( getNativeType(U2U_CONSTRUCTOR_TYPE)); FunctionType findDelegate = typeRegistry.createFunctionType( typeRegistry.createDefaultObjectUnion(delegateBaseObject), functionParamBuilder.build()); FunctionType delegateProxy = typeRegistry.createConstructorType( delegateBaseObject.getReferenceName() + DELEGATE_PROXY_SUFFIX, null, null, null); delegateProxy.setPrototypeBasedOn(delegateBaseObject); codingConvention.applyDelegateRelationship( delegateSuperObject, delegateBaseObject, delegatorObject, delegateProxy, findDelegate); delegateProxyPrototypes.add(delegateProxy.getPrototype()); } } } /** * Declare the symbol for a qualified name in the global scope. * * @param info The doc info for this property. * @param n A top-level GETPROP node (it should not be contained inside * another GETPROP). * @param parent The parent of {@code n}. * @param rhsValue The node that {@code n} is being initialized to, * or {@code null} if this is a stub declaration. */ void maybeDeclareQualifiedName(NodeTraversal t, JSDocInfo info, Node n, Node parent

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS> break; case Token.GETPROP: maybeCollectMember(t, child, child, null); break; } } } private void maybeCollectMember(NodeTraversal t, Node member, Node nodeWithJsDocInfo, @Nullable Node value) { JSDocInfo info = nodeWithJsDocInfo.getJSDocInfo(); // Do nothing if there is no JSDoc type info, or // if the node is not a member expression, or // if the member expression is not of the form: this.someProperty. if (info == null || member.getType() != Token.GETPROP || member.getFirstChild().getType() != Token.THIS) { return; } member.getFirstChild().setJSType(thisType); JSType jsType = getDeclaredType(t.getSourceName(), info, member, value); Node name = member.getLastChild(); if (jsType != null && (name.getType() == Token.NAME || name.getType() == Token.STRING)) { thisType.defineDeclaredProperty( name.getString(), jsType, false /* functions with implementations are not in externs */, member); } } } // end CollectProperties } /** * A stub declaration without any type information. */ private static final class StubDeclaration { private final Node node; private final boolean isExtern; private final String ownerName; private StubDeclaration(Node node, boolean isExtern, String ownerName) { this.node = node; this.isExtern = isExtern; this.ownerName = ownerName; } } /** * A shallow traversal of the global scope to build up all classes, * functions, and methods. */ private final class GlobalScopeBuilder extends AbstractScopeBuilder { private GlobalScopeBuilder(Scope scope) { super(scope); } /** * Visit a node in the global scope, and add anything it declares to the * global symbol table. * * @param t The current traversal. * @param n The node being visited. * @param parent The parent of n */ @Override public void visit(NodeTraversal t, Node n, Node parent) { super.visit(t, n, parent);

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS> switch (n.getType()) { case Token.ASSIGN: // Handle typedefs. checkForOldStyleTypedef(t, n); break; case Token.VAR: // Handle typedefs. if (n.hasOneChild()) { checkForOldStyleTypedef(t, n); checkForTypedef(t, n.getFirstChild(), n.getJSDocInfo()); } break; } } @Override void maybeDeclareQualifiedName( NodeTraversal t, JSDocInfo info, Node n, Node parent, Node rhsValue) { checkForTypedef(t, n, info); super.maybeDeclareQualifiedName(t, info, n, parent, rhsValue); } /** * Handle typedefs. * @param t The current traversal. * @param candidate A qualified name node. * @param info JSDoc comments. */ private void checkForTypedef( NodeTraversal t, Node candidate, JSDocInfo info) { if (info == null || !info.hasTypedefType()) { return; } String typedef = candidate.getQualifiedName(); if (typedef == null) { return; } // TODO(nicksantos|user): This is a terrible, terrible hack // to bail out on recusive typedefs. We'll eventually need // to handle these properly. typeRegistry.declareType(typedef, getNativeType(UNKNOWN_TYPE)); JSType realType = info.getTypedefType().evaluate(scope, typeRegistry); if (realType == null) { compiler.report( JSError.make( t.getSourceName(), candidate, MALFORMED_TYPEDEF, typedef)); } typeRegistry.overwriteDeclaredType(typedef, realType); if (candidate.getType() == Token.GETPROP) { defineSlot(candidate, candidate.getParent(), getNativeType(NO_TYPE), false); } } /** * Handle typedefs. * @param t The current traversal. * @param candidate An ASSIGN or VAR node. */ // TODO(nicksantos): Kill this. private void checkForOldStyleTypedef(NodeTraversal t, Node candidate) { // old-style typedefs String typedef = codingConvention.

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS>identifyTypeDefAssign(candidate); if (typedef != null) { // TODO(nicksantos|user): This is a terrible, terrible hack // to bail out on recusive typedefs. We'll eventually need // to handle these properly. typeRegistry.declareType(typedef, getNativeType(UNKNOWN_TYPE)); JSDocInfo info = candidate.getJSDocInfo(); JSType realType = null; if (info != null && info.getType() != null) { realType = info.getType().evaluate(scope, typeRegistry); } if (realType == null) { compiler.report( JSError.make( t.getSourceName(), candidate, MALFORMED_TYPEDEF, typedef)); } typeRegistry.overwriteDeclaredType(typedef, realType); // Duplicate typedefs get handled when we try to register // this typedef in the scope. } } } // end GlobalScopeBuilder /** * A shallow traversal of a local scope to find all arguments and * local variables. */ private final class LocalScopeBuilder extends AbstractScopeBuilder { /** * @param scope The scope that we're builidng. */ private LocalScopeBuilder(Scope scope) { super(scope); } /** * Traverse the scope root and build it. */ void build() { NodeTraversal.traverse(compiler, scope.getRootNode(), this); } /** * Visit a node in a local scope, and add any local variables or catch * parameters into the local symbol table. * * @param t The node traversal. * @param n The node being visited. * @param parent The parent of n */ @Override public void visit(NodeTraversal t, Node n, Node parent) { if (n == scope.getRootNode()) return; if (n.getType() == Token.LP && parent == scope.getRootNode()) { handleFunctionInputs(parent); return; } super.visit(t, n, parent); } /** Handle bleeding functions and function parameters. */ private void handleFunctionInputs(Node fnNode) { // Handle bleeding functions. Node fnNameNode = fnNode.getFirstChild(); String fnName = fnNameNode.getString

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS>(); if (!fnName.isEmpty()) { Scope.Var fnVar = scope.getVar(fnName); if (fnVar == null || // Make sure we're not touching a native function. Native // functions aren't bleeding, but may not have a declaration // node. (fnVar.getNameNode() != null && // Make sure that the function is actually bleeding by checking // if has already been declared. fnVar.getInitialValue() != fnNode)) { defineSlot(fnNameNode, fnNode, fnNode.getJSType(), false); } } declareArguments(fnNode); } /** * Declares all of a function's arguments. */ private void declareArguments(Node functionNode) { Node astParameters = functionNode.getFirstChild().getNext(); Node body = astParameters.getNext(); FunctionType functionType = (FunctionType) functionNode.getJSType(); if (functionType != null) { Node jsDocParameters = functionType.getParametersNode(); if (jsDocParameters != null) { Node jsDocParameter = jsDocParameters.getFirstChild(); for (Node astParameter : astParameters.children()) { if (jsDocParameter != null) { defineSlot(astParameter, functionNode, jsDocParameter.getJSType(), true); jsDocParameter = jsDocParameter.getNext(); } else { defineSlot(astParameter, functionNode, null, true); } } } } } // end declareArguments } // end LocalScopeBuilder /** Find the best JSDoc for the given node. */ static JSDocInfo getBestJSDocInfo(Node n) { JSDocInfo info = n.getJSDocInfo(); if (info == null) { Node parent = n.getParent(); int parentType = parent.getType(); if (parentType == Token.NAME) { info = parent.getJSDocInfo(); if (info == null && parent.getParent().hasOneChild()) { info = parent.getParent().getJSDocInfo(); } } else if (parentType == Token.ASSIGN) { info = parent.getJSDocInfo(); } else if (NodeUtil.isObjectLitKey(parent, parent

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS>/* * Copyright 2008 The Closure Compiler Authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.google.javascript.jscomp; import com.google.javascript.jscomp.CheckLevel; /** * Class that allows to flexibly manage what to do with a reported * warning/error. * * Guard has several choices: * - return OFF - suppress the warning/error * - return WARNING * - return ERROR report it with high severity * - return null. Does not know what to do with it. Lets the other guard * decide what to do with it. * * Although the interface is very simple it allows you easyly customize what * warnings you are interested in. * * For example there are could be several implementations: * StrictGuard - {return ERROR}. All warnings should be treat as errors. * SilentGuard - {if (WARNING) return OFF}. Suppress all warnings but still * fail if js has errors. * WhitelistGuard (if !whitelistErrors.contains(error) return ERROR) return * error if it does not present in the whitelist. * * @author anatol@google.com (Anatol Pomazau) */ public abstract class WarningsGuard { public static enum Priority { MAX(1), MIN(100), STRICT(100), DEFAULT(50), SUPPRESS_BY_WHITELIST(40), SUPPRESS_DOC(20), FILTER_BY_PATH(1); final int value; Priority(int value) { this.value = value; } public int getValue() { return value;

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS> } } /** * Returns a new check level for a given error. OFF - suppress it, ERROR - * report as error. null means that this guard does not know what to do * with the error. Null is extremely helpful when you have a chain of * guards. If current guard returns null, then the next in the chain should * process it. * * @param error a reported error. * @return what level given error should have. */ public abstract CheckLevel level(JSError error); /** * The priority in which warnings guards are applied. Lower means the * guard will be applied sooner. Expressed on a scale of 1 to 100. */ protected int getPriority() { return Priority.DEFAULT.value; } /** * Returns whether all warnings in the given diagnostic group will be * filtered out. Used to determine which passes to skip. * * @param group A group of DiagnosticTypes. * @return Whether all warnings of these types are disabled by this guard. */ protected boolean disables(DiagnosticGroup group) { return false; } /** * Returns whether any of the warnings in the given diagnostic group will be * upgraded to a warning or error. * * @param group A group of DiagnosticTypes. * @return Whether any warnings of these types are enabled by this guard. */ protected boolean enables(DiagnosticGroup group) { return false; } }

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS> where the {@code params} is a comma * separated list of types, the first one being a special * {@code this:T} if the function expects a known type for {@code this}. */ @Override public String toString() { if (this == registry.getNativeType(JSTypeNative.FUNCTION_INSTANCE_TYPE)) { return "Function"; } StringBuilder b = new StringBuilder(32); b.append("function ("); int paramNum = call.parameters.getChildCount(); boolean hasKnownTypeOfThis = !typeOfThis.isUnknownType(); if (hasKnownTypeOfThis) { if (isConstructor()) { b.append("new:"); } else { b.append("this:"); } b.append(typeOfThis.toString()); } if (paramNum > 0) { if (hasKnownTypeOfThis) { b.append(", "); } Node p = call.parameters.getFirstChild(); if (p.isVarArgs()) { appendVarArgsString(b, p.getJSType()); } else { b.append(p.getJSType().toString()); } p = p.getNext(); while (p != null) { b.append(", "); if (p.isVarArgs()) { appendVarArgsString(b, p.getJSType()); } else { b.append(p.getJSType().toString()); } p = p.getNext(); } } b.append("): "); b.append(call.returnType); return b.toString(); } /** Gets the string representation of a var args param. */ private void appendVarArgsString(StringBuilder builder, JSType paramType) { if (paramType.isUnionType()) { // Remove the optionalness from the var arg. paramType = ((UnionType) paramType).getRestrictedUnion( registry.getNativeType(JSTypeNative.VOID_TYPE)); } builder.append("...[").append(paramType.toString()).append("]"); } /** * A function is a subtype of another if their call methods are related via * subtyping and {@code this} is a subtype of {@code that} with regard to * the prototype chain. */ @

Closure, 157

<FILEB>
<CHANGES>
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
<CHANGEE>
<CHANGES>
Preconditions.checkState(c.getType() == Token.STRING);
String key = c.getString();
<CHANGEE>
<CHANGES>
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
<CHANGEE>
<CHANGES>
NodeUtil.isLatin(key)) {
add(key);
<CHANGEE>
<CHANGES>
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1);
}
<CHANGEE>
<CHANGES>
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (TokenStream.isJSIdentifier(child.getString())) {
<CHANGEE>
<CHANGES>
if (TokenStream.isJSIdentifier(key.getString())) {
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo!= null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
<CHANGEE>
<CHANGES>
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
<CHANGEE>
<CHANGES>
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
<CHANGEE>
<CHANGES>
Preconditions.checkState(ret.getType() == Token.STRING);
<CHANGEE>
<FILEE>
<FILEB> Preconditions.checkState(first.getType() == Token.FUNCTION); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GET) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. <CHANGES> if (TokenStream.isJSIdentifier(name) && <CHANGEE> // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. <CHANGES> add(jsString(n.getString(), outputCharsetEncoder)); <CHANGEE> } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.getType() == Token.GET || c.getType() == Token.SET) { add(c); } else { <CHANGES> <CHANGEE> // Object literal property names don't have to be quoted if they // are not JavaScript keywords <CHANGES> if (c.<SCANS> hasKnownTypeOfThis = !typeOfThis.isUnknownType(); if (hasKnownTypeOfThis) { b.append("this:"); b.append(getDebugHashCodeStringOf(typeOfThis)); } if (paramNum > 0) { if (hasKnownTypeOfThis) { b.append(", "); } Node p = call.parameters.getFirstChild(); b.append(getDebugHashCodeStringOf(p.getJSType())); p = p.getNext(); while (p != null) { b.append(", "); b.append(getDebugHashCodeStringOf(p.getJSType())); p = p.getNext(); } } b.append(")"); b.append(": "); b.append(getDebugHashCodeStringOf(call.returnType)); return b.toString(); } private String getDebugHashCodeStringOf(JSType type) { if (type == this) { return "me"; } else { return type.toDebugHashCodeString(); } } }